Skip to content
Open
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
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,9 @@ dl 0.59.2
Projects with several variants, compose sidecars, or a host-side `initializeCommand` are covered
in [docs/devcontainer-projects.md](docs/devcontainer-projects.md).

`--claude-profile <name>` forwards a named Claude login instead of the default one, for
workspaces where you want a different account than the one `claude` on your host is signed in to:
`--claude-profile <name>` binds a Claude configuration directory into the container. For a named
profile that replaces forwarding your host's login, for workspaces where you want a different
account than the one `claude` on your host is signed in to:

```bash
dl blooop/devlaunch --claude-profile work
Expand All @@ -363,13 +364,25 @@ so profiles you already have work with no re-login, and it never writes there: c
deleting them stays with whatever made the directory. By hand it is
`CLAUDE_CONFIG_DIR=~/.claude-profiles/work claude`, then log in.

The whole directory is bound in, not just the credential: any `CLAUDE.md`, agents, skills or
hooks living beside it reach the container too, and a token refresh in there lands back in
the profile on disk. A bind lands only when the container is created, so switching profiles on a
workspace that already exists needs a `recreate`; a plain `restart` keeps whichever profile the
container was created with.

`--claude-profile default` means the login you would get anyway, so a recalled line has a way to
say "not the profile I used last time".
say "not the profile I used last time". It still binds: your host's own Claude configuration
directory (`$CLAUDE_CONFIG_DIR`, or `~/.claude`) is mounted read-write into the container, the
same as a named profile's directory, so `CLAUDE.md`, agents, skills and hooks beside it reach the
container too, and a refresh in there lands back on the host.

Unlike `--devcontainer` it is **not** stored with the workspace, so it applies to the launch you
typed it on and no workspace ever forwards an account chosen weeks ago. A name that holds no
credential stops the launch and says so rather than falling back to your default login, which is
the whole point of naming one. [docs/workspace-tools.md](docs/workspace-tools.md) has the
typed it on and no workspace ever forwards an account chosen weeks ago. A named profile that holds
no credential stops the launch and says so rather than falling back to your default login, which is
the whole point of naming one. `default` is the one exception: a host whose Claude login is not a
credential file in that directory falls back to forwarding the login as before, with nothing
mounted and nothing said, since `default` names the login you would get anyway rather than an
account you are asking `dl` to find. [docs/workspace-tools.md](docs/workspace-tools.md) has the
precedence order and what a profile does not change.

`dl --help` is the complete reference and is kept in step with the binary by a test.
Expand Down
6 changes: 3 additions & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ once under the `api` section and once under the module that owns them.

**What it still does not reach, and it is not one type.** A type `api` never re-exports but a
promised signature hands back is reachable from outside and classified as binary surface. Counted
on the checked-in files rather than guessed at, that is **40 types owning close to six hundred rows** in
on the checked-in files rather than guessed at, that is **41 types owning close to six hundred rows** in
`public-api.rest.txt`, and the command that lists them needs no toolchain:

**A falling count is not automatically a win, and it is worth knowing which kind you are looking
Expand All @@ -93,10 +93,10 @@ file that moves is the one this page calls freely regenerated.
section used to give on its own, which made six hundred rows read as one.

So a diff in `public-api.rest.txt` is routine for a row whose subject nothing promised names, and a
contract change for a row whose subject is one of the 38. `--print-residual` is how you tell the two
contract change for a row whose subject is one of the 41. `--print-residual` is how you tell the two
apart, and `test/test_public_api_snapshots_doc.py` diffs the count of types in this paragraph
against it, so the sentence goes red rather than stale. The row total is left round on purpose: it
moves whenever anything is added to any one of the 38 and says nothing about the scale of the
moves whenever anything is added to any one of the 41 and says nothing about the scale of the
limit, where the count of types moves only when the residual really grows.

The `-ss` flag also omits blanket and auto-trait impls from both files,
Expand Down
20 changes: 16 additions & 4 deletions docs/workspace-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,17 +172,24 @@ logged in while holding a perfectly good login.

### Naming a profile

`--claude-profile <name>` forwards a named login instead of the default one, for the
case the order above cannot serve: two accounts on one machine, and a workspace
that wants the one your host is not signed in to.
`--claude-profile <name>` binds a named configuration directory into the container
instead of forwarding the default login, for the case the order above cannot
serve: two accounts on one machine, and a workspace that wants the one your host
is not signed in to.

```bash
dl owner/repo --claude-profile work
```

The name is one directory under `~/.claude-profiles/`, holding the
`.credentials.json` a `claude` login writes. Each such directory is a
`CLAUDE_CONFIG_DIR` of its own, which is what makes the logins independent.
`CLAUDE_CONFIG_DIR` of its own, which is what makes the logins independent. The
whole directory is bound in, not only the credential, so a `CLAUDE.md`, agents,
skills or hooks living beside it reach the container too, and a token
refresh in there lands back in the profile on disk rather than going stale. A
bind lands only when the container is created, so switching to a different
profile on a workspace that already exists is a `recreate`; a `restart` keeps
whichever profile the container was created with.

**That is somebody else's directory and `dl` only reads it.** The layout and the
`CLAUDE_PROFILES_DIR` variable belong to the tool that manages them, honoured here
Expand All @@ -208,6 +215,11 @@ next.
`--claude-profile default` resolves the login you would get anyway and never consults
a `default/` directory. It exists as a word because a picker needs something to
select, and a recalled line needs a way to say "not the profile I used last time".
It still binds that login's own configuration directory into the container,
read-write, the same as a named profile does, whenever a credential file sits
in it; a host whose Claude login is not a credential file in that directory
falls back to forwarding the login with nothing mounted, silently, since
there is no name here for a launch to refuse over.

### Seeing which account a profile actually holds

Expand Down
48 changes: 48 additions & 0 deletions rust/devlaunch-core/public-api.api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,29 @@ pub devlaunch_core::api::LaunchNotice::CheckoutBehind::ahead: u32
pub devlaunch_core::api::LaunchNotice::CheckoutBehind::behind: u32
pub devlaunch_core::api::LaunchNotice::CheckoutBehind::branch: alloc::string::String
pub devlaunch_core::api::LaunchNotice::CheckoutBehind::workspace_id: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::credential_bind: core::option::Option<std::path::PathBuf>
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::extra_binds: alloc::vec::Vec<std::path::PathBuf>
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::extra_binds_capped: bool
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::extra_binds_refused: bool
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileBound::source: std::path::PathBuf
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountSwitched
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountSwitched::bound: std::path::PathBuf
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountSwitched::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountSwitched::requested: std::path::PathBuf
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUidMismatch
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUidMismatch::container_uid: u32
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUidMismatch::dir_uid: u32
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUidMismatch::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUidMismatch::target: std::path::PathBuf
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUnappliable
pub devlaunch_core::api::LaunchNotice::ClaudeProfileMountUnappliable::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileNotForwarded
pub devlaunch_core::api::LaunchNotice::ClaudeProfileNotForwarded::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileSourceUnsafe
pub devlaunch_core::api::LaunchNotice::ClaudeProfileSourceUnsafe::name: alloc::string::String
pub devlaunch_core::api::LaunchNotice::ClaudeProfileSourceUnsafe::source: std::path::PathBuf
pub devlaunch_core::api::LaunchNotice::CodexCredentialUnreadable
pub devlaunch_core::api::LaunchNotice::CodexCredentialUnreadable::path: alloc::string::String
pub devlaunch_core::api::LaunchNotice::CodexStageMissing
Expand Down Expand Up @@ -445,6 +466,7 @@ impl<'e> devlaunch_core::flows::launch::ToolProvisioning<'e>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'e>::for_verb(self, &devlaunch_core::flows::launch::LaunchVerb) -> Self
pub fn devlaunch_core::flows::launch::ToolProvisioning<'e>::from_env(&std::path::Path, &'e mut dyn devlaunch_core::notices::Notices<devlaunch_core::flows::provision::ProvisionEvent>) -> Self
impl devlaunch_core::flows::launch::Provision for devlaunch_core::flows::launch::ToolProvisioning<'_>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
Expand All @@ -468,11 +490,13 @@ pub trait devlaunch_core::api::Notices<T>
pub fn devlaunch_core::api::Notices::say(&mut self, T)
impl<T> devlaunch_core::notices::Notices<T> for alloc::vec::Vec<T>
pub trait devlaunch_core::api::Provision
pub fn devlaunch_core::api::Provision::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::api::Provision::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::api::Provision::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::api::Provision::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
pub fn devlaunch_core::api::Provision::stage_missing_for_this_launch(&self, &str) -> bool
impl devlaunch_core::flows::launch::Provision for devlaunch_core::flows::launch::ToolProvisioning<'_>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
Expand Down Expand Up @@ -603,8 +627,29 @@ pub devlaunch_core::flows::launch::LaunchNotice::CheckoutBehind::ahead: u32
pub devlaunch_core::flows::launch::LaunchNotice::CheckoutBehind::behind: u32
pub devlaunch_core::flows::launch::LaunchNotice::CheckoutBehind::branch: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::CheckoutBehind::workspace_id: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::credential_bind: core::option::Option<std::path::PathBuf>
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::extra_binds: alloc::vec::Vec<std::path::PathBuf>
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::extra_binds_capped: bool
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::extra_binds_refused: bool
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileBound::source: std::path::PathBuf
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountSwitched
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountSwitched::bound: std::path::PathBuf
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountSwitched::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountSwitched::requested: std::path::PathBuf
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUidMismatch
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUidMismatch::container_uid: u32
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUidMismatch::dir_uid: u32
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUidMismatch::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUidMismatch::target: std::path::PathBuf
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUnappliable
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileMountUnappliable::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileNotForwarded
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileNotForwarded::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileSourceUnsafe
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileSourceUnsafe::name: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::ClaudeProfileSourceUnsafe::source: std::path::PathBuf
pub devlaunch_core::flows::launch::LaunchNotice::CodexCredentialUnreadable
pub devlaunch_core::flows::launch::LaunchNotice::CodexCredentialUnreadable::path: alloc::string::String
pub devlaunch_core::flows::launch::LaunchNotice::CodexStageMissing
Expand Down Expand Up @@ -727,6 +772,7 @@ impl<'e> devlaunch_core::flows::launch::ToolProvisioning<'e>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'e>::for_verb(self, &devlaunch_core::flows::launch::LaunchVerb) -> Self
pub fn devlaunch_core::flows::launch::ToolProvisioning<'e>::from_env(&std::path::Path, &'e mut dyn devlaunch_core::notices::Notices<devlaunch_core::flows::provision::ProvisionEvent>) -> Self
impl devlaunch_core::flows::launch::Provision for devlaunch_core::flows::launch::ToolProvisioning<'_>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
Expand All @@ -738,11 +784,13 @@ impl<'r> devlaunch_core::flows::launch::ColdMachinery<'r> for devlaunch_core::fl
pub fn devlaunch_core::flows::launch::ColdPath<'r, '_>::open(&mut self) -> core::result::Result<devlaunch_core::flows::launch::Cold<'_, 'r>, devlaunch_core::flows::launch::ColdRefused>
pub fn devlaunch_core::flows::launch::ColdPath<'r, '_>::recorded(&mut self) -> core::option::Option<&devlaunch_core::domain::metadata::MetadataStorage>
pub trait devlaunch_core::flows::launch::Provision
pub fn devlaunch_core::flows::launch::Provision::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::flows::launch::Provision::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::flows::launch::Provision::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::flows::launch::Provision::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
pub fn devlaunch_core::flows::launch::Provision::stage_missing_for_this_launch(&self, &str) -> bool
impl devlaunch_core::flows::launch::Provision for devlaunch_core::flows::launch::ToolProvisioning<'_>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::last_claude_mount(&self) -> devlaunch_core::flows::provision::ClaudeMountFacts
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::pass_never_finished(&self, &str) -> bool
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::provision_tools(&self, &dyn devlaunch_runner::Runner, &str, devlaunch_core::flows::provision::PassOccasion, core::option::Option<&str>) -> core::result::Result<core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>, devlaunch_core::flows::provision::DevpodMissing>
pub fn devlaunch_core::flows::launch::ToolProvisioning<'_>::remembered_claude(&self, &str) -> core::option::Option<devlaunch_core::flows::provision::ClaudeConfig>
Expand Down
Loading
Loading