Skip to content
Merged
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ npm i @openmaxai/openmax-agent-sdk
> resolves it via the `latest` dist-tag. Pre-1.0 alphas remain available under the
> `alpha` dist-tag for history.

> Cutting a new version? See **[`RELEASING.md`](./RELEASING.md)** for the exact
> files to bump and the release flow.

**In the SDK** (generic CWS + agent-level concerns):

- **transport/** — `WsClient` (auth, heartbeat, client keepalive-ping + frame-watchdog, exponential-backoff reconnect, 4001–4006 close-code handling), HTTP client (native `fetch` + auth), CF-Access headers, token/identity management.
Expand Down
70 changes: 70 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Releasing / bumping the SDK version

This is the checklist for cutting a new version of
`@openmaxai/openmax-agent-sdk`. Follow it exactly — the version string lives in
more than one place on purpose, and CI will fail if they drift.

## 1. Files to change on every version bump

| File | What to change | Enforced by |
| --- | --- | --- |
| `package.json` | `version` field | `release.yml` refuses to publish unless the git tag matches this |
| `package-lock.json` | root `version` **and** `packages[""].version` — just run `npm install` to regenerate both | — |
| `src/index.js` | the `SDK_VERSION` **literal** — must equal `package.json` `version` | `src/version.test.js` (CI fails on any mismatch) |
| `README.md` | the "first stable release" note near the top and the release-summary paragraph near the bottom, **if** the human-facing notes change | — (prose; not machine-checked) |

That's the whole set. A single `npm install` after editing `package.json`
handles the lockfile; the other two are hand edits.

## 2. Do NOT

- **Do not turn `SDK_VERSION` back into a runtime read of `package.json`**
(e.g. `createRequire(import.meta.url)('../package.json').version`). It reads
cleanly when the SDK runs from its own installed package, but it does **not
survive bundling**: a consumer that inlines the SDK into a self-contained
artifact carries the read into their bundle, where `../package.json` fails to
resolve at load time. That regression is exactly why `SDK_VERSION` is a
literal today. Keep it a literal; `src/version.test.js` is the drift guard so
you get the anti-drift guarantee without the runtime read.
- **Do not touch these — they are not the package version:**
- `src/orchestrator.js` `reporters.version` (a reporter payload default,
supplied by the host adapter).
- `CONTRACT.md` / `schemas/v1/**` `version` fields — the protocol contract is
versioned **independently** of the npm package.

## 3. Release steps

1. Branch off `main`; bump the files in §1.
2. Verify locally: `npm test` (must be green, incl. the `SDK_VERSION` drift
guard) and — if you touched anything the scanners see — the same Semgrep
command CI runs.
3. Push, open a PR. **Required CI must pass**: `test (node 20)`,
`test (node 22)`, `semgrep`, `gitleaks`. Get an approval from **someone
other than the last pusher** (org branch ruleset). Merge to `main`.
4. Tag the merged commit and push the tag:
```bash
git tag -a vX.Y.Z <merged-main-sha> -m "vX.Y.Z"
git push origin vX.Y.Z
```
The tag **must** match `package.json` `version` (release.yml checks this) and
point at a commit contained in protected `main` (release.yml checks this too
— an unmerged commit is refused).
5. Pushing `v*` triggers **`release.yml`**, which pauses at the `release`
environment approval gate. A reviewer approves the deployment, then it runs
`npm publish --provenance`.
6. **dist-tag is automatic** from the version shape:
- stable (no hyphen, e.g. `1.0.1`) → published to **`latest`**;
- prerelease (a hyphen, e.g. `1.1.0-alpha.0`) → published to **`alpha`**,
and `latest` is left untouched (a stable already exists).

## 4. Fixing a dist-tag after the fact

Use the **`promote-dist-tag`** workflow (Actions → Run workflow). It moves a
dist-tag (e.g. `latest`) onto an already-published version from CI, gated by the
same `release` environment — no local `npm login` needed. It refuses to point a
tag at a version that was never published.

## 5. Versioning scheme

Semver. Prereleases use `-alpha.N`. Patch = fixes (incl. behavior fixes for
consumers, like a bundling fix); minor = additive API; major = breaking changes.
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openmaxai/openmax-agent-sdk",
"version": "1.0.0",
"version": "1.0.1",
"description": "CWS agent runtime SDK — cws-comm protocol layer (WS/auth/heartbeat/reconnect, sync, message codec, tm/kb/as/comm/core/conn service clients) extracted from zylos-openmax; consumed by runtime adapters.",
"type": "module",
"main": "src/index.js",
Expand Down
11 changes: 7 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
* Scaffold. Modules are re-exported here as they are extracted from
* zylos-openmax (Phase A). Current tranche: providers.
*/
import { createRequire } from 'node:module';

export * from './providers.js';

// ── transport layer (Phase A · milestone 1) ─────────────────────────────────
Expand Down Expand Up @@ -70,5 +68,10 @@ export * from './identity/self-name-hydration.js'; // createSelfNameHydrator
// CLI shells) stays in the adapter behind the injected providers/callbacks.
export * from './orchestrator.js'; // CwsAgentBridge

// Sourced from package.json so it never drifts from the released version.
export const SDK_VERSION = createRequire(import.meta.url)('../package.json').version;
// Hardcoded literal, NOT a runtime read of package.json. A `createRequire(...)
// ('../package.json')` here does not survive bundling: when a consumer inlines
// the SDK into a self-contained artifact, that call is preserved and resolves
// `../package.json` relative to the CONSUMER's bundle at runtime — which fails
// when the bundle is loaded in isolation. The `SDK_VERSION` test asserts this
// literal stays in sync with package.json, so it can never silently drift.
export const SDK_VERSION = '1.0.1';
15 changes: 15 additions & 0 deletions src/version.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createRequire } from 'node:module';
import { SDK_VERSION } from './index.js';

// `SDK_VERSION` is a hardcoded literal in src/index.js — deliberately NOT a
// runtime `createRequire('../package.json')`, because that call does not survive
// bundling: a consumer inlining the SDK into a self-contained artifact would
// carry the read into their bundle, where `../package.json` fails to resolve at
// runtime. This test is the drift guard — reading package.json here is safe
// because tests run in-repo (package.json present) and are never bundled.
test('SDK_VERSION matches package.json version', () => {
const pkg = createRequire(import.meta.url)('../package.json');
assert.equal(SDK_VERSION, pkg.version);
});
Loading