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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
- name: Lint
run: bun run lint

# Catch npm-version drift between the CLI and its workspace packages on
# the PR, not at tag time when the release is already in flight.
- name: Version lockstep
run: bun run check:versions

- name: Typecheck
run: bun run typecheck

Expand Down
65 changes: 23 additions & 42 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

permissions:
contents: write # create the GitHub Release
id-token: write # npm provenance (public repo)
id-token: write # npm OIDC Trusted Publishing + provenance

jobs:
npm:
Expand Down Expand Up @@ -34,55 +34,36 @@ jobs:
- name: Install dependencies
run: bun install --frozen-lockfile

# Every publishable package ships at the SAME version, and the tag is that
# version. One gate replaces per-package version bookkeeping: if this
# passes, all three publishes below are known-correct and known-new.
- name: Verify workspace versions are in lockstep with the tag
run: bun run check:versions "${GITHUB_REF_NAME}"

- name: Build web UI
run: cd web && bun install --frozen-lockfile && bun run build

- name: Test
run: bun run test

- name: Verify package version matches the tag
run: |
PKG="$(node -p "require('./package.json').version")"
TAG="${GITHUB_REF_NAME#v}"
if [ "$PKG" != "$TAG" ]; then
echo "::error::package.json version ($PKG) does not match tag ($TAG)"
exit 1
fi

# @codeoid/protocol is a workspace dependency of codeoid; publish it FIRST
# (only when its version is new) so codeoid's published dep resolves.
# NOTE: requires a separate npm Trusted Publisher configured for
# @codeoid/protocol at npmjs.com (same OIDC setup as codeoid) before the
# first release that introduces a new protocol version.
- name: Publish @codeoid/protocol (OIDC) — only if version is new
run: |
cd packages/protocol
VER="$(node -p "require('./package.json').version")"
if npm view "@codeoid/protocol@$VER" version >/dev/null 2>&1; then
echo "@codeoid/protocol@$VER already published — skipping"
else
npm publish --provenance --access public
fi
# Publish order is dependency order: protocol → core → the CLI, so each
# tarball's declared deps are resolvable on the registry the moment it
# lands. No NODE_AUTH_TOKEN anywhere — auth is the GitHub OIDC id-token
# via each package's Trusted Publisher (configured at npmjs.com), and
# provenance is generated from that same identity. `--access public` is
# not passed: every manifest carries `publishConfig.access`, so a manual
# publish and this pipeline run the exact same command.
#
# A new package NAME requires one manual bootstrap publish before a
# Trusted Publisher can be configured for it — see RELEASING.md.
- name: Publish @highflame/codeoid-protocol
run: cd packages/protocol && npm publish --provenance

# @codeoid/core (client transport + store semantics — consumed by the
# mobile app from the registry; web uses the in-repo copy). Publishes
# after protocol (its peer dep) and needs its own Trusted Publisher +
# one-time manual bootstrap publish, same as protocol.
- name: Publish @codeoid/core (OIDC) — only if version is new
run: |
cd packages/core
VER="$(node -p "require('./package.json').version")"
if npm view "@codeoid/core@$VER" version >/dev/null 2>&1; then
echo "@codeoid/core@$VER already published — skipping"
else
npm publish --provenance --access public
fi
- name: Publish @highflame/codeoid-core
run: cd packages/core && npm publish --provenance

# No NODE_AUTH_TOKEN: auth comes from the GitHub OIDC id-token via npm's
# Trusted Publisher (configured on the package at npmjs.com). Provenance
# is generated automatically from the same OIDC identity.
- name: Publish codeoid to npm (OIDC Trusted Publishing)
run: npm publish --provenance --access public
- name: Publish @highflame/codeoid
run: npm publish --provenance

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
Expand Down
52 changes: 52 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,58 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.4.0] - 2026-07-29

codeoid moves to the Highflame npm org. npm has no way to transfer a package
between scopes, so all three published packages are renamed rather than moved,
and their versions are unified onto one line so a single tag cuts the whole
release.

### Changed

- **Renamed on npm** — the packages now ship from the `@highflame` scope:

| Before | After |
| ------------------- | ----------------------------- |
| `codeoid` | `@highflame/codeoid` |
| `@codeoid/protocol` | `@highflame/codeoid-protocol` |
| `@codeoid/core` | `@highflame/codeoid-core` |

Install with `bun install -g @highflame/codeoid`. The binary is still
`codeoid`, and no runtime behaviour changed. The predecessor packages are
deprecated on npm and frozen at `0.3.4` / `0.2.0` / `0.2.0`; existing installs
keep resolving. Downstream consumers (the mobile app, any external importer)
need to update their dependency names — the import specifiers changed with the
package names.

- **Lockstep versioning.** All three packages now ship at the same version from
one `vX.Y.Z` tag, replacing the per-package "publish only if the version is
new" logic in the release workflow. `bun run check:versions` enforces it on
every PR, in `bun run smoke`, and against the tag at release time;
`bun run version:set <x.y.z>` is the only sanctioned way to bump. The wire
protocol version (`PROTOCOL_VERSION`) remains a separate, independently-moving
number.

### Fixed

- **The client core was an undeclared dependency of the CLI.** The TUI renderer
and the Telegram frontend both statically import `formatCollaborationCost` from
the client core (added in 8e709ec, after `0.3.4`), but it was never listed in
the CLI's `dependencies` — only the protocol package was. A registry install
would have had no client core in `node_modules` and failed to resolve the
import on any code path reaching either module, `codeoid attach` among them.
Caught before it shipped: `0.3.4` predates the import and is unaffected. Now
declared, and verified by installing the packed tarball and resolving that
module.

- **`web/` no longer needs the registry to install.** `web/` is a separate
install root, so the workspace packages come in as `file:` copies — but the
core package's peer range on the protocol package was still resolved from the
registry, meaning a fresh `bun install` in `web/` depended on what had been
published rather than on the working tree (and broke outright for a
not-yet-published package name). An `overrides` entry pins that peer to the
in-repo copy.

## [0.3.4] - 2026-07-23

The governed pipeline becomes reliable end-to-end. Phase boundaries are now
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Memory persists across sessions, so each agent inherits what the last one learne
Every action is auditable, and with [ZeroID](https://github.com/highflame-ai/zeroid) every agent and sub-agent carries a cryptographic identity.

```bash
bun install -g codeoid
bun install -g @highflame/codeoid
codeoid start --local # no account, no login, no config — running in seconds
```

Expand Down Expand Up @@ -67,12 +67,16 @@ Don't mix the two.
Codeoid runs on Bun, so install it with Bun (`npm` also works, as long as Bun is on your `PATH` — it's the runtime):

```bash
bun install -g codeoid # or: npm install -g codeoid
codeoid --help # confirm it's on your PATH
bun install -g @highflame/codeoid # or: npm install -g @highflame/codeoid
codeoid --help # confirm it's on your PATH
```

If `codeoid` isn't found afterward, your global-bin directory isn't on `PATH` — add Bun's (`~/.bun/bin`) to it, or use path **B**.

> **Renamed.** codeoid used to publish as the unscoped `codeoid` package. It now
> ships from the Highflame org as **`@highflame/codeoid`**; the old package is
> deprecated on npm and stops at `0.3.4`. The command is still `codeoid`.

**B. From source — to hack on it.**
Clone, then **run `bun install` before anything else**:

Expand Down
112 changes: 93 additions & 19 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,41 @@
# Releasing codeoid

Releases are automated. Pushing a `vX.Y.Z` tag publishes `codeoid` to npm with
[provenance](https://docs.npmjs.com/generating-provenance-statements) and opens a
GitHub Release — see [`.github/workflows/release.yml`](.github/workflows/release.yml).
Three packages ship from this repo, all under the `@highflame` npm scope:

## One-time setup
| Package | Directory | Who consumes it |
| ------------------------------ | ------------------- | ------------------------------------------------ |
| `@highflame/codeoid` | repo root | end users — installs the `codeoid` CLI + daemon |
| `@highflame/codeoid-protocol` | `packages/protocol` | the daemon, the web UI, the mobile app |
| `@highflame/codeoid-core` | `packages/core` | the daemon, the web UI, the mobile app |

Add an **`NPM_TOKEN`** repository secret (Settings → Secrets and variables →
Actions → New repository secret):
## The one rule: lockstep versions

1. On npmjs.com → **Access Tokens** → create a **Granular** (or Automation) token
with publish rights for the `codeoid` package.
2. Save it as `NPM_TOKEN`.
**All three packages always ship at the same version, cut from one `vX.Y.Z` tag.**

That is the entire contract the pipeline rests on. One tag produces three
publishes in dependency order, with no per-package "is this version new?"
bookkeeping and no drift to reconcile by hand. `bun run check:versions` enforces
it — in CI on every PR, in `bun run smoke`, and again in the release workflow
against the tag itself — so a partial bump fails long before it reaches npm.

Bump with the script, never by hand; it rewrites all three `version` fields plus
the internal `^` dependency and peer ranges in one pass:

```bash
bun run version:set 0.5.0
bun install && (cd web && bun install) # refresh both lockfiles
```

> **The wire protocol version is a separate number.** `PROTOCOL_VERSION` in
> [`packages/protocol/src/types.ts`](packages/protocol/src/types.ts) moves only on
> wire-breaking changes, and stays in lockstep with the `codeoid-protocol` crate
> in [codeoid-ui](https://github.com/highflame-ai/codeoid-ui). npm versions being
> in lockstep says nothing about wire compatibility, and bumping one is never a
> reason to bump the other — the handshake negotiates.

## Cutting a release

1. Bump `version` in [`package.json`](package.json) (SemVer).
1. `bun run version:set X.Y.Z`, then refresh both lockfiles.
2. Move the `## [Unreleased]` notes into a new `## [X.Y.Z]` section in
[`CHANGELOG.md`](CHANGELOG.md).
3. Open a PR, get CI green, merge to `main`.
Expand All @@ -27,21 +47,75 @@ Actions → New repository secret):
git push origin vX.Y.Z
```

The workflow then builds the web UI, runs the test suite, verifies the tag matches
`package.json`, `npm publish`es, and creates the GitHub Release. Install with:
[`.github/workflows/release.yml`](.github/workflows/release.yml) then builds the
web UI, runs the test suite, verifies the three versions match the tag, publishes
protocol → core → CLI with
[provenance](https://docs.npmjs.com/generating-provenance-statements), and opens a
GitHub Release. Install with:

```bash
bun install -g codeoid # or: bunx codeoid
bun install -g @highflame/codeoid # or: bunx @highflame/codeoid
```

The binary is still called `codeoid`.

## One-time setup — npm Trusted Publishing (OIDC)

There are **no npm tokens** in this repo. Publishing authenticates with the
GitHub Actions OIDC id-token against a Trusted Publisher configured on each
package at npmjs.com, and provenance is generated from that same identity.

npm can only attach a Trusted Publisher to a package that **already exists**, so
each new package name needs exactly one manual bootstrap publish first. This is a
per-package-name chore, not a per-release one.

**Prerequisite:** publish rights on the `@highflame` npm org (an org member with
at least write access to the packages, or an org admin who can create them).

```bash
npm login # must be the @highflame org member

bun install
cd web && bun install --frozen-lockfile && bun run build && cd ..
bun run check:versions

# Dependency order. No --provenance: that requires a CI OIDC token and will
# fail locally. No --access public: publishConfig.access covers it.
cd packages/protocol && npm publish && cd ../..
cd packages/core && npm publish && cd ../..
npm publish # @highflame/codeoid — runs prepublishOnly (web build)
```

Then, for **each** of the three packages, at
`npmjs.com/package/<name>/access` → **Trusted Publisher** → *GitHub Actions*:

| Field | Value |
| ----------------- | --------------- |
| Organization | `highflame-ai` |
| Repository | `codeoid` |
| Workflow filename | `release.yml` |
| Environment | *(leave empty)* |

After that, every subsequent release goes through the tag → workflow path above
and nothing needs a token again.

## Notes

- **codeoid runs under [Bun](https://bun.sh).** The published package ships `src/`
- **codeoid runs under [Bun](https://bun.sh).** The published CLI ships `src/`
plus the prebuilt `web/dist`, and points `bin` at `src/cli.ts` (Bun executes
TypeScript directly — no bundling step, and the web UI path resolves inside the
package).
- The **wire protocol** is versioned independently via `PROTOCOL_VERSION` in
[`src/protocol/types.ts`](src/protocol/types.ts). Bump it only on wire-breaking
changes, and keep it in lockstep with the `codeoid-protocol` crate in
[codeoid-ui](https://github.com/highflame-ai/codeoid-ui). App versions and the protocol
version move independently; the handshake negotiates compatibility.
- **`web/` is its own install root** with its own lockfile, so the workspace
packages come in as `file:` copies rather than workspace links. Bun *copies*
them at install time, so after editing `packages/protocol` or `packages/core`
you must re-run `bun install` in `web/` to refresh the copy. The `overrides`
entry in [`web/package.json`](web/package.json) pins the transitive
`@highflame/codeoid-protocol` peer to the in-repo copy too — without it, a
fresh `bun install` in `web/` would try to satisfy that peer from the registry
and the web build would silently depend on what is published rather than on the
working tree.
- **Predecessor packages.** codeoid was previously published from a personal
account as `codeoid`, `@codeoid/protocol`, and `@codeoid/core`. npm has no way
to move a package between scopes, so those names were retired in favour of the
`@highflame` ones and deprecated in place (existing installs keep resolving).
The last version published under the old names was `0.3.4` / `0.2.0` / `0.2.0`.
23 changes: 12 additions & 11 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading