The Codeman Agent Skill setting is now on by default, and the per-case
copy it writes lists twice next to the Claude Code plugin (codeman and
codeman:codeman), the same as a codeman skill install would. The README
and the wiki now say so and tell plugin users to turn the setting off.
The README's local model bullet said the session "sends nothing off the
machine". The server keeps the runtime on loopback with its cloud
features off, but opencode itself is not configured offline (its model
catalogue and update checks are untouched), so the bullet now says what
the code does: the prompts and files go to a model on this Mac.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured on opencode 1.3.0 in a private tmux socket, with a fake
OpenAI-compatible endpoint streaming the reply a character or two at a
time: the marker was on the screen, but the stripped client stream read
D, spinner, O, spinner, N, ... because opencode redraws its spinner
between the streamed pieces, so wait-output never matched it. sendmark
then waited its whole timeout and answered 2 for a reply that had
finished, with the answer file already written (opencode's write tool
wrote it without asking).
The marker wait now runs in rounds of at most 10 s, and between rounds
the answer file is a way out too: a non-empty regular file whose
checksum held still for one round is read, with matched false. The
marker stays the fast path. The answer-file wait after a marker no
longer spins when the exit wait gets no answer, and treats an ended
wait as the worker gone.
The same run measured the readiness mark: with a fresh home and a
custom provider, the first composer bar came with the composer and a
prompt pasted with its Enter at that moment was submitted, so the docs
say so instead of "not measured". The example keeps its input in a
subfolder, apart from the case's CLAUDE.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The architecture note now says that install and uninstall refuse a linked
folder inside the copy, why (a cloned repository or a Docker case's
container can plant one), and that the check is not race-proof. It also
records which CLIs were checked live with the copy's ignore file in place:
Claude Code 2.1.319, Codex 0.162, OpenCode 1.3.0 and Grok 1.0.52 all list
the skill; agy, dsh, gemini and omp were not measured.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot's registry entry takes no agent skill, like Pi and a plain shell, but
the changeset, the App Settings help text, the Settings Reference and
CLAUDE.md named only Pi (and the shell). The Agent CLIs wiki page still said
no CLI but Claude gets the bundled skill; every CLI except Pi and Copilot
gets it, in `.agents/skills/`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five gate files created sessions with `workingDir: '/tmp'` against a real
server. With the agent skill on by default, every `npm test` then wrote a
skill copy into `/tmp/.claude/skills/codeman/`, outside the per-file temp
HOME, where a Claude Code started in `/tmp` would load it (the workspace
hooks already landed in `/tmp/.claude/settings.local.json` the same way).
Each file now uses a temp folder of its own and removes it afterwards.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Install and uninstall checked only the copy and its parent for symlinks, so a
`reference` folder that is a link carried every write and unlink of
`reference/<name>.md` to wherever it pointed. Measured: install overwrote a
`history.md` outside the workspace and uninstall deleted it. A cloned
repository can commit that link, and a Docker case's container can plant it
in the bind-mounted workspace the host injects into. With the skill on by
default, both now ran without the user asking.
Both paths now return 'symlink' for a linked folder inside the copy, before
the first write or unlink, and the refusal messages say so.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sendmark's comment and verbs.md 5.5 listed six external modes by name,
and test/agent-skill-mode-lists.test.ts refuses any partial list of them
(the one a ninth CLI would silently miss). Both now say what the class
is: a hook-less claude, a worker on the local model, and every external
agent mode but deepseek.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agentSkillEnabled now reads an absent key as ON, the way
workspaceHooksEnabled does: only an explicit false turns it off. A new
session therefore finds the codeman skill in its folder without a
setting first, and with it the verbs that hand work to other CLIs and to
the local model on this Mac. Settings shows the switch on for the same
absent key.
The injection itself is unchanged: add-only, marker-owned, never through
a symlink, never for remote sessions or a plain shell. Turning the
setting off still removes nothing already written.
The quick-start test now pins the new default (injected on an absent
key, nothing after an explicit false), and the docs that said "default
off" say when that changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A row can pin its build only as `digest`, the value /api/tags reports
after the pull, and LocalModelEntrySchema must gain that optional field
before the weekly check can compare it. The script header and the docs
now say so, so the field cannot arrive under another name and be ignored.
Until it exists, the report's digest table names the build a Mac pulls
for each row and asks for the rows with measured figures first: their
memory figures describe one build, and without a digest a rebuild of
about the same size goes unnoticed. A switch between the mlx and the gguf
build is still caught by the size check (2% or more apart).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
scripts/check-local-model-catalog.mjs reads every catalogue tag the way
Ollama 0.40.15 on Apple silicon reads it: it asks for the manifest list,
takes the mlx build when the tag offers one (else llamacpp, else ggml),
and compares that build's pull total (layers plus config) with the row's
`bytes`. It also reports a tag that is gone, a build whose `requires` is
newer than the pinned runtime, a pinned digest that moved, a newer Ollama
release, and a pinned ollama-darwin.tgz that is gone or changed. It never
requests a layer, bounds every request, retries nothing and validates
everything that reaches the report. Exit 0 clean, 1 drift, 2 failed.
The plain Docker manifest of a list tag is the llamacpp build, not what a
Mac pulls. Read the right way, the first run finds two rows sized from the
wrong build: qwen3.5:9b (a Mac downloads 7,639,229,431 bytes, +16.6%) and
qwen3.8:27b (18,174,721,847, +2.4%).
.github/workflows/local-model-catalog.yml runs it Mondays at 06:00 UTC
and on dispatch. The check job installs with --ignore-scripts and holds a
read-only token; a separate job with issues: write and no checkout opens
or updates the one "Local model catalogue: weekly check" issue on drift,
comments on it on a clean week when it is open, and is skipped when the
check failed. Actions are pinned by commit.
docs/local-models.md gains "Keeping the catalogue current".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/local-models.md says how a Claude Code session drives the model on
this Mac through the skill (local_model, spawn_worker local, sendmark)
and what stays true: the skill never installs a model or answers a
confirmation, one request at a time, and Stop and Remove wait for the
workers it opened.
The README gets the local model as a feature with that pairing, and an
example prompt in the skill's table; the wiki's skill page gets the same
row and a short paragraph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SKILL.md gets a subsection after the DeepSeek Harness workers: why to hand
work to the model on this Mac (bulk work with no per-token bill, files
that stay on the machine, a second opinion, a worker that needs no
network), the copy-paste block (local_model, spawn_worker local, sendmark,
delete_session), and the limits as the code has them: a person installs
the model, one request at a time, slower than the cloud, markers not
hooks, a spawn can start a runtime the person stopped, admin only and
experimental.
The description now names the local model, so "use the local model"
reaches the skill. Section 2 gains a row, the hook-less row points at
sendmark, and reference/verbs.md gets 5.16 with every local_model reason,
the spawn steps and the sendmark exit codes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The codeman skill's preamble learns the model Codeman installs on this Mac:
- local_model asks GET /api/v1/local-model and prints one JSON line: the
installed model, the coding agent that runs it and its endpoint, or the
reason there is none (not a Mac, nothing installed, an unfinished
install, a blocked install, admin only, a server without the routes).
Exit 0 only when a worker can start now or after a runtime start.
- spawn_worker <name> local (and name:local in spawn_workers) starts a
stopped runtime the way the window's Start button does, quick-starts the
installed agent with customModel, and waits for its composer (OpenCode's
bar, the claude path for a claude agent, LOCAL_READY_MARK otherwise). A
confirmation question from quick-start fails the spawn with empty stdout;
nothing is ever confirmed on the person's behalf.
- sendmark <sid> <prompt> drives any agent worker without an end-of-turn
signal: the exactly-once input body, a split marker through wait-output,
then the answer read back from a file the worker wrote in its folder
(regular files only, 256 KB at most), with one exit code per outcome.
The preamble stamp moves to 1.50.0 so every cached copy is rewritten.
The tests run the real preamble against a stubbed server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every place that called the Codeman Agent Skill setting off by default now
says it is on by default since 1.50.0, where to turn it off, and that turning
it off still removes no copy already written (`codeman skill uninstall --case
<name>` does). The README, the wiki's Codeman Skill page and the FAQ say an
agent inside Codeman usually has the skill without an install step, and that
the copy ignores itself in git.
Also written down: an install whose App Settings were saved on an earlier
version stored the old default and stays off until switched on; with the
Claude Code plugin installed, Claude Code lists the skill twice (both work);
and, in the invariants, the self-ignoring `.gitignore` rules and the
measurement that Claude Code 2.1.319 never runs `git check-ignore` on a skill
folder. The agent control plan keeps its original default-off decision and
gains a dated note for the flip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An absent `agentSkillEnabled` now means on, on the server
(`getAgentSkillEnabled()` reads `!== false`, like `workspaceHooksEnabled`)
and on the page (the App Settings switch loads `!== false`, so a Save of any
other setting no longer writes the old default back). Only an explicit false
turns it off. A partial settings PUT that omits the key leaves it as stored,
since the route merges the request into the file.
The switch's help text says it is on by default, that the copy stays out of
git, and that turning it off removes no copy already written. The shared
route-test mock now defaults to on too, so route tests see what a user sees.
Tests: quick-start against a real server (absent key injects, a partial PUT
keeps it on, an explicit false stays off through a later partial PUT), the
real openAppSettings/saveAppSettings in JSDOM (absent shows on, explicit false
shows off, a Save on a never-touched install sends true and passes the
schema), and the plain create route at the mock's default.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Codeman-managed copy of the agent skill (`.claude/skills/codeman/`,
`.agents/skills/codeman/`, and the user-level copy) now carries a `.gitignore`
of `*`, written once with O_EXCL before any skill file, the way the prompt
uploads folder does. A user's repository no longer lists the copy as
untracked, and Codeman never touches the repository's own ignore files.
A file already at that name is never overwritten, a copy injected before this
gains the file on its next install (reported as refreshed), and uninstall
removes it only when it is the last thing left and holds exactly `*`, so a
user's extra files in the folder stay ignored. Claude Code 2.1.319 runs
`git check-ignore` only on directories at or above `.claude/skills`, never on
a skill folder, so the skill still loads.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The desktop README's Known limits still said an agent is added by hand after the install, and the wiki gave the menu item a shorter name than the one the app shows.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The welcome screen's "Start Ralph Loop" button is gone (chore/remove-ralph-start-button), and it was the wizard's only entry point, so the page no longer offers the wizard as a second way in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"A MacBook Pro whose Apple logo lights up" also describes the 2013 to 2015
Retina MacBook Pros, whose lid logo glows from the display backlight and which
run macOS 11 or 12, both of which the app supports. The View menu's Blink
Apple Logo exists only where CoreBrightness answers for the lid logo
(logo::available() in packages/desktop/src/logo.rs), which is a MacBook Pro
with a PROLED XDR display. The README, its Chinese mirror and the wiki now use
packages/desktop/README.md's wording, so the wiki's "the menu only exists on
such a Mac" holds.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A dispatch with -f tag=<any tag> built that tag, and sign then ran the tag's
sign-macos.sh with the Developer ID key unlocked and the notary login in its
environment. Nothing checked the tag: xterm-zerolag-input@0.1.7 would have put
Codeman.dmg on that package's release, and a tag on a commit that never
reached master would have run unreviewed scripts with the secrets.
build and sign now each check their own checkout before anything else runs:
the tag must read codeman@x.y.z (or aicodeman@x.y.z, which a failed rename
leaves) and GitHub's compare of master with the checked-out commit must say
behind or identical. The check is inline, since a script from the checkout
would be the tag's own code. Release's own call (a push, not a dispatch) and
the branch dry run are unchanged.
The header says why a master-only Environment would not close this path, and
release.yml's macos-app job now says that any permission a called job asks
for must be granted there too, or Release fails at startup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The welcome screen's "Start Ralph Loop" button is gone (chore/remove-ralph-start-button), and it was the wizard's only entry point, so the page no longer offers the wizard as a second way in.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
outsideWorktreeOptions() now returns undefined for an adopted session as it does for a remote or Docker one: session.remote and session.docker are undefined on an adopted session, so it needs its own test. An adopted pane gets no paneCwd today (paneExitApplies), so nothing changes in practice; the rule stays true if that ever changes.
The live-worktree.ts @fileoverview said the routes re-check only the realpath of an outside worktree. They also judge the attachment guard rules again on every request (isOutsideWorktreeRootRefused() through withGuardedOutsideWorktree()), which is what makes a confinement or blocked-tree change apply at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two stubs from #776 cover the global ignore file and the global config. A core.excludesFile in the system gitconfig still reached the code under test and failed the same test (reproduced with GIT_CONFIG_SYSTEM pointing at one). GIT_CONFIG_NOSYSTEM closes that, as test/git-hooks.test.ts already does for its code under test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
release.yml and docs/releasing.md take the branch's text, which is master's text plus the macOS app additions (its first commit copied both files from master).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved as on test/desktop-macbook: config/test-suites.ts keeps both browser tests (desktop-connect-page and local-model-ui), and the CLAUDE.md state-files line keeps #778's cli-installed entry with the local model's local-model.json and local-model/ entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved as on test/desktop-macbook: install.sh usage() lists CODEMAN_NO_ADMIN=1 or 0 and #773's CODEMAN_AI_CLI and CODEMAN_PROGRESS=1; probe.rs keeps #781's agent() with TIMEOUT, MAX_BODY and probe_within all pub; the desktop README keeps both known-limits entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every Codeman release now carries the Mac app as Codeman.dmg and
Codeman.dmg.sha256, under a name that never changes, so
https://github.com/Ark0N/Codeman/releases/latest/download/Codeman.dmg always
serves the newest app.
.github/workflows/desktop-dmg.yml (workflow_call from Release with
tag codeman@<version>; workflow_dispatch with an optional tag) has three jobs,
so no job that holds a secret runs code from a crate:
- build (macos-26, contents: read, no secret, no cache) runs cargo test --locked
and bundle-macos.sh --universal ad hoc, with CODEMAN_DESKTOP_INSTALLER_URL,
CODEMAN_DESKTOP_INSTALL_BRANCH and SIGN_IDENTITY unset, checks both slices,
the signature and that no test installer URL or branch is baked in, and
uploads the app as a ditto zip.
- sign (macos-26, contents: read) refuses an artifact with an entry outside
Codeman.app, a ".." or a symlink, imports MACOS_CERT_P12 into a keychain under
RUNNER_TEMP with a random password, picks the one Developer ID Application
identity by its hash and checks it belongs to APPLE_TEAM_ID, runs sign-macos.sh
with the Apple ID login and uploads the signed pair as the codeman-dmg
artifact. Its last step deletes the keychain and the decoded .p12 whatever
happened before.
- attach (ubuntu, contents: write, no secret) runs only with a tag, on master
(Release itself, or a rebuild started there) and in Ark0N/Codeman; it uploads
the pair with --clobber and checks that the release's download link, and the
Latest one when this release holds Latest, serve those bytes.
A run without a tag is the dry run: everything but attach.
release.yml reads the Codeman version from changesets' publishedPackages
(published alone is also true for an xterm-zerolag-input-only release) and runs
the macOS app job only for a Codeman release, passing it the five secrets by
name, never secrets: inherit. A new step puts Latest back on the newest
codeman@ release after every publish, so a release that moved only
xterm-zerolag-input no longer takes the badge and breaks the stable link. Both
new steps sit after the retag and only warn on failure, so npm, the tag and the
MCP publish are never held up by them.
desktop-ci.yml checks pull requests and master pushes that touch
packages/desktop: cargo fmt --check, clippy -D warnings, cargo test --locked and
bash -n under macOS's bash 3.2, on macos-26 with a read-only token. Both
workflows pin Rust 1.99.1, what the macos-26 image and the Mac mini ran on
2026-11-08.
docs/releasing.md: step 6 holds a site change that links the app until the DMG
check passes, step 7 names the new job, and a new section covers the secrets,
the check, a dry run, a rebuild and what to do when notarization fails.
actionlint 1.7.12 (with shellcheck 0.11.0) reports nothing on the three
workflows, and master's test/release-notes.test.ts assertions hold on the new
release.yml.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The release DMG is signed with the owner's Developer ID and notarized, so a
downloaded copy opens after macOS's usual confirmation instead of stopping at
Gatekeeper. The work is a script of its own, so CI and a Mac run it the same way:
SIGN_IDENTITY=... NOTARY_PROFILE=<profile> \
packages/desktop/scripts/sign-macos.sh <Codeman.app> <out-dir>
It re-signs the app with the hardened runtime, a secure timestamp and
macos/entitlements.plist, checks the signature is a Developer ID one with the
runtime flag, has notarytool notarize it and staples the ticket, builds the DMG
around the stapled app, signs it, notarizes and staples it too, and has spctl
assess both. Codeman.dmg and Codeman.dmg.sha256 reach <out-dir> only after every
check passed; a failed run removes an older pair there rather than leave one to
upload by mistake. A submission Apple does not accept stops the script with its
id and Apple's log. The notary login is either a notarytool keychain profile
(NOTARY_PROFILE, NOTARY_KEYCHAIN) or APPLE_ID, APPLE_APP_PASSWORD and
APPLE_TEAM_ID; `sign-macos.sh --check` tests that without doing anything.
The DMG layout (the app next to an /Applications link, UDZO, volume "Codeman")
moves to make-dmg.sh, which both scripts call, and gains a retry: hosted macOS
runners now and then fail hdiutil create with "Resource busy". bundle-macos.sh
keeps its SIGN_IDENTITY path as it was; its new --notarize checks the
environment before building, then hands the app to sign-macos.sh.
packages/desktop/README.md says the release DMG is notarized and how to do
the same by hand, and drops the CI build and notarization from its ideas list.
All three scripts are bash 3.2 clean, and shellcheck reports nothing. With the
macOS tools stubbed, sign-macos.sh was run under bash 3.2 through a full
signing, a notarization rejected for the app and for the DMG, two hdiutil
failures then a pass, three failures, a relative out-dir and every missing
setting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch starts from feat/desktop-app (#763), which predates master's MCP
registry job and the release-notes body in release.yml and their pages in
docs/releasing.md. Both files are master's text verbatim here, so the Mac app
commit after this one changes them by additions only, and a rebase onto master
drops this commit as empty.
release.yml now names .github/workflows/publish-mcp.yml and
scripts/generate-release-notes.mjs, which exist on master but not on this
branch. The Release workflow runs only on pushes to master, so that matters
nowhere else.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 1.50.0 release attaches Codeman.dmg to every release, so
https://github.com/Ark0N/Codeman/releases/latest/download/Codeman.dmg
always serves the newest app. This puts that link where a Mac user
looks first:
- README.md: one line under the install one-liner, and a short
"On a Mac: the Codeman app" section in Quick Start, after Docker
Compose. What the app does, the download, universal (Apple Silicon
and Intel), macOS 11.3, the WebKit of Safari 16.4 for Codeman's page,
macOS 15 (or a tmux already there) for the install from the app, and
how to update it.
- README.zh-CN.md: the same, translated.
- docs/wiki/Installation.md: a matching section with a First launch
list (the one "downloaded from the internet" question, the Local
Network question on macOS 15) and the limits that matter on day one.
Its anchors, #on-a-mac-the-codeman-app and #first-launch, are what
the website links for first-launch help. Route A points to it.
The limits are copied from packages/desktop/README.md. The app is
signed with a Developer ID and notarized, so no Gatekeeper workaround
and no per-update Keychain prompt is described.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The logo said one thing: agents are working. It now has a style of its own
for each state the tabs show: at work, finished and waiting for you (yellow),
and stopped on a permission or a question (red), set under View > Blink Apple
Logo. The most urgent state with a style wins, red then yellow then work, and
a state set to Off falls through to the next instead of silencing the logo.
Red and yellow come from `GET /api/approvals`, the list the page rebuilds
those tabs from after a reload, read natively for the reason the session list
is: WebKit stalls a hidden page's stream. An idle item stops counting once it
is acknowledged, so the logo stops when the tab's colour goes. The request is
skipped while neither alert has a style, a 404 from an older server reads as
no alerts, and a request that fails keeps the last answer. A dialog on screen
costs the server one tmux capture per poll.
Alarm is new: three sharp flashes and a short rest. It stays under the limit
of three flashes in any second, so its urgency comes from its shape.
A style saved by the earlier version stays as the one for work, and yellow
and red start as Wink and Alarm, since whoever chose one asked for a logo that
talks. `SETTLE` goes from 2 s to 3 s, the time the server takes to judge a new
dialog, so a prompt answered before its hook landed never flashes red.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`GET /api/approvals` re-reads the pane of every pending dialog and resolves
one whose frame shows no dialog, either because its options vanished or
because the frame shows a turn running. `notePrompt` deliberately waits 3 s
before making that same judgement, since the Notification hook can land
before Ink paints the dialog and the working line is still on screen then.
The list did not wait. That rarely mattered while it ran once per page load,
but a client that polls it would resolve a live prompt as answered in the
terminal, clearing the red alert on every device and the push Approve button
with it. `sweepForList` skips a dialog younger than the delay.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a MacBook Pro with a PROLED XDR display, the lit Apple logo on the lid
can blink while agents work and stop when they finish, so a run can be
read from across the room. View > Blink Apple Logo While Agents Work
picks a style (Calm, Slow, Wink, Heartbeat, Fast). It is off by default,
and the menu exists only on a Mac that has the logo.
Apple documents one control for the logo, `sudo defaults write
com.apple.CoreBrightness LEBacklightMode -int 0`. The same setting is a
property of BrightnessSystemClient in the private CoreBrightness
framework, which a process of the logged-in user can set with no
administrator, so nothing here needs a helper or a password.
The setting is system-wide and survives a reboot, and the release profile
has panic = "abort", so the work runs in a child process (this binary
with --logo-blink) tied to the app by a pipe. When the pipe closes,
however the app ended, the child restores the logo and exits. A marker
file covers power loss and a SIGKILL of the child itself.
The child asks api/sessions itself: WebKit stalls the event stream of a
hidden page, which lost whole turns and ran a blink 39 s past the end.
It sends the session cookie of its first answer with every poll, because
a good password without a cookie creates a login session each time and
Codeman evicts the oldest of 100.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a Codeman server of this user ran at an address the app could not open
as its default one, the This Mac tab only named host and port and sent the
user to Another machine to type the address again. A second instance beside
the one that answers (a beta, a `codeman web --port 4000` in a terminal) was
not shown at all.
The look at this Mac now works out an address for every listening socket of
this user's Codeman servers (`found_addresses`, pure): 127.0.0.1 for a bind
to loopback or to every interface, `[::1]` when that is the only loopback
socket, the address itself for a bind to one; `https` and the base path come
from `--https` and `--base-url` on the command line. A server at the default
address with no `--base-url` of its own keeps the default URL, since
install.sh's launchd job has the base path in its environment, where `ps`
does not show it. The default address comes first, then the others by port.
Each address other than the default one is asked once, all at the same time,
with no login. The default probe's answer is reused for its own row. When
nothing answers at the default address, the first found server that answers
is still what Open is for, so each gets the probe's usual 5 s; next to a
Codeman that answers they are only listed and get 2 s. `LocalStatus.found`
replaces `elsewhere`.
The tab lists them: "Codeman is already running on this Mac." with a row
each when none answers at the default address, and "Also running on this
Mac" under Open otherwise. A row has the address, the version and Connect,
or the reason the app cannot open it. Connect fills the address in and runs
the page's one connect(), so the app checks it, uses its saved login and
remembers it like an address typed under Another machine. Nothing connects
by itself, and a refused connect from a row never arms the reopen that Open
has for a starting server.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The violet button was the one thing on the start screen outside the app's
blue, and it carried white text and a see-through disc where its neighbour
has dark ink and a solid disc. It is now white with dark ink and a solid
disc, the same on the Install button, and the chooser's selected row, tile
and model bar follow. Light skins turn it over to dark, since a white button
vanishes on a pale page. The context takes the app's blue.
The tokens are named for what they mean now (--lm-brand, --lm-context).
Brings test/local-model-ui.browser.test.ts up to the redesign it had fallen
behind: it is outside the gate, so the button's 46 px, the 12 px bar and the
memory panel's edges had gone unnoticed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>