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 .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ node_modules/
.env.*
!.env.example
*.tgz

.loader-*/
module-data/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Add independently packable Codex Plugin Loader with desktop/CLI entry, per-module service processes, isolated renderer globals, scoped RPC/events and bounded cleanup.
- Run Tags through the Loader SDK with local settings, catalog and search services.
- Verify service crash containment, cancellation, multiple windows, reload and independent package consumption.

## Unreleased

- Scope sidebar tag counts to mounted rows so counts match the available sidebar filter; retain the complete catalog in the dashboard.

- Filter every mounted sidebar row, including tasks absent from the local catalog and duplicate appearances.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ npx @c0sc0s/codex-tags@latest

Open **Codex → Plugins → Codex Tags** and review/trust **SessionStart**, **UserPromptSubmit**, and **SessionEnd**.

**Next time:** open `~/Applications/Codex Tags.app` and pin it to the Dock. It launches the official app, not a second Codex installation. No automatic restart or launch supervisor. Naming is agent-assisted, not a guaranteed title rewrite.
**Next time:** open `~/Applications/Codex Tags.app` and pin it to the Dock. This is the Codex Plugin Loader entry: it starts the official app and loads configured modules, including Tags. The existing app path is retained for Dock compatibility. No automatic restart or launch supervisor. Naming is agent-assisted, not a guaranteed title rewrite.

<details>
<summary>Develop or install from source</summary>
Expand Down Expand Up @@ -85,3 +85,5 @@ The fast loop requires an already activated, debug-enabled app. No HMR server is
- [Roadmap](docs/roadmap.md) · [Changelog](CHANGELOG.md)

Not affiliated with or endorsed by OpenAI. No open-source license is currently granted (`UNLICENSED`).

See [Codex Plugin Loader](docs/plugin-loader.md) for the independent package and module SDK: Loader manages CDP, isolated service processes and RPC/events; modules implement business behavior.
4 changes: 3 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ npx @c0sc0s/codex-tags@latest

打开 **Codex → Plugins → Codex Tags**,检查并信任/启用 **SessionStart、UserPromptSubmit、SessionEnd**。

**下次启动:** 使用 `~/Applications/Codex Tags.app`,可拖到 Dock 固定。它启动的是官方 App,不是第二套 Codex;不会自动重启或安装启动守护进程。命名由 Agent 辅助完成,不保证每次确定性改名。
**下次启动:** 使用 `~/Applications/Codex Tags.app`,可拖到 Dock 固定。它是 Codex Plugin Loader 的入口:启动官方 App 后,由 Loader 加载 Tags 等已配置模块,保留原路径以兼容 Dock;不会自动重启或安装启动守护进程。命名由 Agent 辅助完成,不保证每次确定性改名。

<details>
<summary>从源码开发或安装</summary>
Expand Down Expand Up @@ -85,3 +85,5 @@ npm run test:package
- [后续规划](docs/roadmap.md) · [更新记录](CHANGELOG.md)

本项目独立开发,不隶属于 OpenAI,也未经其背书。目前没有开放源代码许可授权(`UNLICENSED`)。

独立基础包与模块开发接口见 [Codex Plugin Loader](docs/plugin-loader.md):Loader 管理 CDP、独立服务进程和 RPC/事件,业务模块通过 SDK 实现功能。
64 changes: 20 additions & 44 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,32 @@
# Architecture

[Development](development.md) · [Protocol](protocol.md) · [Roadmap](roadmap.md)
[Development](development.md) · [Protocol](protocol.md) · [Loader SDK](plugin-loader.md)

Codex Tags is a reversible enhancement, not a Codex fork.
The desktop launcher starts Codex Plugin Loader. Loader loads configured modules into the official Codex app without modifying its signed bundle.

## Ownership

| Layer | Owns | Must not own |
| --- | --- | --- |
| CLI / manager | Installation, activation, removal, diagnostics | Session naming or UI behavior |
| Dedicated launcher | Explicitly start the official app with loopback debugging | Monitoring launches or restarting a running app |
| Controller services | CDP targets, settings, catalog, search index | DOM selectors or UI state |
| Injected UI | Presentation, interactions, reversible decoration | Filesystem access or durable settings |
| Host adapter | Codex selectors and native row bindings | Classification policy |
| Hooks / skills | Live classification guidance for the agent | Direct transcript/title database writes |

## Data flow
| Layer | Responsibility |
| --- | --- |
| Tags CLI / installer | Package installation, module configuration and diagnostics |
| Desktop launcher | Invoke the standalone Loader CLI |
| Loader | Owned CDP endpoint, isolated renderer world, per-plugin service processes, RPC/events, lifecycle and cleanup |
| Tags service | Local settings, session catalog, search index and navigation validation |
| Tags renderer | UI, interactions and reversible DOM decoration |
| Codex DOM adapter | All private host selectors and native row bindings |
| Hooks / skills | Agent naming guidance using the saved tag definitions |

```text
Codex state database ──read-only──▶ SessionCatalog ──metadata──┐
Codex session JSONL ──read-only──▶ SQLite FTS5 ──snippets──────┤
▼
settings.json ◀── SettingsRepository ◀── ControllerRouter ⇄ injected UI
│ │
└── hook / naming skills → Codex agent └── host adapter
Desktop entry → Loader → Tags renderer ⇄ RPC/events ⇄ Tags service
│ ├─ settings.json
Codex DOM adapter ├─ read-only session catalog
└─ local SQLite search index
```

The active local catalog is independent of sidebar expansion. Remote-only sessions remain best-effort DOM discovery. Schema mismatch reports an incomplete catalog and falls back to visible/cached rows. Conversation text stays in the local index; only bounded matching snippets cross the bridge.

## Resource boundaries

- **SettingsRepository:** normalization, migration, serialized atomic writes. Renderer storage is only a cache; concurrent windows currently use last-writer-wins.
- **SessionCatalog:** read-only schema-checked metadata, changed snapshots about every 5 seconds. Excludes subagents and internal guardian reviews using `thread_source` and legacy `source` provenance; standalone agent-created tasks remain visible. Filtered snapshots prune cached local entries, keeping counts and search scope consistent.
- **SessionRegistry:** joins metadata and temporary native bindings using canonical local IDs.
- **SessionSearchIndex:** incremental FTS5 indexing, with discovery/refresh about every 30 seconds and bounded text extraction.
- **CodexProcess / TargetRegistry:** process ownership, target discovery, versioned injection and client cleanup.
- **HostLifecycle:** coalesced native changes and pointer/input-safe refresh.
- **DashboardView:** modal controls and interaction state; Preact result rows. Background updates preserve IME, menus, drafts and scroll.
- **ControllerRouter:** validated intent-shaped messages. Navigation requires a UUID present in the current catalog.

## Stack and evolution

Browser: strict TypeScript, Preact result components, bundled Motion, esbuild IIFE. Controller/CLI: Node ESM and better-sqlite3. No remotely loaded runtime scripts.

The dashboard mixes imperative controls and Preact rows. Migrate to a single Preact root when interaction complexity justifies it; do not introduce a general framework solely for uniformity.

## Safety
`runtime/src/plugin-loader` is independently packable and has no Tags or SQLite dependency. Its public module contexts expose business RPC, events and resource lifecycle; business modules never construct CDP commands or injection expressions. Services run in separate Node processes. Renderer globals live in a named isolated world, sharing the app's DOM and renderer thread. See [Loader architecture](plugin-loader.md) for contracts and failure behavior.

Native title DOM and listeners have restoration paths. Missing host capabilities should disable the enhancement without damaging native navigation. The signed bundle, session records and authentication data remain untouched.
Tags registers `runtime/dist/injected.js` and `tags-service.mjs` in `loader.json`. On activation the renderer requests its configuration; after readiness the service sends settings/catalog snapshots. Each connected window has an instance identity, so replacement and reload cannot receive another instance's outstanding replies.

`Codex Tags.app` explicitly launches the official app with loopback debugging. If a non-debuggable Codex is already open, activation stops with instructions to quit it manually. No launch supervisor is installed; upgrades unload and remove the legacy LaunchAgent. Updates stop old code before replacing files and are retryable, not automatically rolled back.
`SettingsRepository` normalizes and atomically serializes writes. Windows use last-writer-wins; localStorage is a cache. Hooks read the same settings file. `SessionCatalog` reads schema-checked local metadata independently of sidebar expansion; schema mismatch reports incompleteness. `SessionRegistry` joins that metadata with temporary DOM bindings. `SessionSearchIndex` refreshes about every 30 seconds; catalog snapshots refresh about every five seconds. Only metadata and bounded matching snippets enter the renderer, and conversation content stays local.

Private DOM/schema/CDP dependencies cannot be guaranteed across future Codex releases. Keep them at adapter/process/catalog boundaries and verify [compatibility](compatibility.md).
`HostLifecycle` coalesces native changes. `DashboardView` combines imperative controls and Preact result rows, preserving input composition, drafts, menus and scroll during updates. `ControllerRouter` validates Tags message envelopes; navigation requires a UUID in the current catalog. Private selectors stay in `injected/codex-dom-adapter.ts`.

For every new capability, identify its owner, command/message, failure isolation, cleanup and tests. Reuse shared normalization; never add another settings store, selectors outside the adapter, or complete-transcript transfer.
Browser code uses strict TypeScript, Preact, bundled Motion and an esbuild IIFE. Node services use ESM and better-sqlite3. Modules register cleanup as they acquire resources. The Loader contains service-process failures; renderer plugins remain trusted code sharing DOM and CPU. No remote runtime assets are loaded. Signed app files, sessions and authentication data remain untouched.
23 changes: 15 additions & 8 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ Edit this checkout, never installed files in Application Support or the plugin c
npm run dev:apply
```

This runs build → repository `install` → `apply` against an already debug-enabled app. No watcher/HMR is provided. Unlike public CLI installation, repository `node scripts/manage.mjs install` only refreshes files and the dedicated launcher and removes the legacy supervisor; it does not register/enable the plugin or activate the UI.
This runs build → repository `install` → `apply` against an already debug-enabled app. No watcher/HMR is provided. Unlike public CLI installation, repository `node scripts/manage.mjs install` stops loaded code, refreshes files and the Loader launcher, and removes the legacy supervisor; it does not register/enable the plugin or activate the UI.

- Browser changes: bump `RUNTIME_VERSION` in `runtime/src/inject-expression.mjs`, then build/install/apply.
- Controller changes: install/apply so the controller uses the updated installed modules.
- Browser changes: bump `RUNTIME_VERSION` in `runtime/src/tags-plugin.mjs`, then build/install/apply.
- Loader kernel changes: bump `LOADER_VERSION` and its package version so the isolated-world bootstrap is replaced after unloading.
- Service/Loader changes: install/apply to stop old modules before replacing files and start the updated Loader.
- Hook/skill changes: refresh the plugin cachebuster and use the public installer; renewed hook review may be required.
- Never edit `runtime/dist/injected.js` manually; commit the generated bundle with source changes.

Expand All @@ -37,8 +38,10 @@ This runs build → repository `install` → `apply` against an already debug-en
| --- | --- |
| CLI and installation | `bin/codex-tags.mjs`, `scripts/{cli-options,manager-core}.mjs` |
| Readiness / mutation lock | `scripts/{health,lifecycle-lock}.mjs` |
| App lifecycle / CDP | `runtime/src/{codex-process,cdp-client,runtime-target-registry}.mjs` |
| Controller / bridge | `runtime/src/{controller,controller-router,protocol}.mjs` |
| Independent loader / CDP | `runtime/src/plugin-loader/` (standalone npm package) |
| Tags module metadata | `runtime/src/tags-plugin.mjs` |
| Tags services / bridge | `runtime/src/{tags-service,controller-router,protocol}.mjs` |
| Tags CLI compatibility | `runtime/src/controller.mjs` |
| Catalog / search | `runtime/src/{session-catalog,content-index,search-index}.mjs` |
| Saved definitions | `runtime/src/{settings-repository,tag-settings}.mjs` |
| Host selectors | `runtime/src/injected/codex-dom-adapter.ts` |
Expand Down Expand Up @@ -66,9 +69,9 @@ App QA requires an already injected app. It checks IME, search, menu persistence

Start with `node bin/codex-tags.mjs doctor --json`.

Logs under `~/Library/Application Support/Codex Sidebar Tags/`: `controller.log`, `launcher.log`. Installation metadata is in `install.json`. Never share credentials or conversation text in diagnostics.
Logs under `~/Library/Application Support/Codex Sidebar Tags/`: `.loader-<identity>/loader.log`, `launcher.log`. Installation metadata is in `install.json`. Never share credentials or conversation text in diagnostics.

Renderer diagnostics: `window.__codexSidebarTags.status()`, `debug()`, `dispose()`.
Renderer diagnostics in the `codex-plugin-loader` isolated world: `window.__codexSidebarTags.status()`, `debug()`, `dispose()`.

| Symptom | Check |
| --- | --- |
Expand All @@ -81,4 +84,8 @@ Renderer diagnostics: `window.__codexSidebarTags.status()`, `debug()`, `dispose(

Testing overrides: `CODEX_TAGS_INSTALL_DIR` (runtime), `CODEX_TAGS_APPLICATIONS_DIR` (launcher), `CODEX_TAGS_CDP_PORT` (default 9341), `CODEX_HOME` (Codex data), `CODEX_TAGS_SETTINGS_PATH` (hook settings), `CODEX_TAGS_STATE_DIR` (hook markers). They do not isolate every macOS/plugin side effect; unit tests use injected fake process runners.

Use `node bin/codex-tags.mjs off` to disable all components while keeping settings. Uninstall only with explicit user permission.
Use `node bin/codex-tags.mjs off` to disable Tags while keeping settings and other Loader modules. Uninstall only with explicit user permission.

## Loader development

See [Loader architecture and entry](plugin-loader.md). The package under `runtime/src/plugin-loader` has no Tags or SQLite dependency. Changes there are copied by the repository installer and included in package smoke. Its Node tests run in `npm test`.
14 changes: 9 additions & 5 deletions docs/distribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Contract

The npm package `@c0sc0s/codex-tags` carries the CLI, prebuilt UI bundle, local controller, naming hooks and three English skills. Its production dependency is native SQLite. Users need macOS, Node.js 22+, and the official Codex app with plugin support.
The npm package `@c0sc0s/codex-tags` carries the CLI, prebuilt UI bundle, independent Loader and Tags service module, naming hooks and three English skills. Its production dependency is native SQLite. Users need macOS, Node.js 22+, and the official Codex app with plugin support.

Installation:

Expand All @@ -13,15 +13,15 @@ Installation:

The CLI uses official plugin commands, never private trust records or authorization bypasses. Installing only the plugin does not provide the native runtime needed for UI injection.

For future launches, open `~/Applications/Codex Tags.app` (pin it to the Dock). This small launcher starts the official app with loopback debugging; it never monitors or restarts a running app. If a non-debuggable Codex is open, it shows a prompt to quit it manually. The official entry is unmodified. The controller only maintains UI injection while the explicitly activated app runs; it does not relaunch Codex. Keep the activation Node installation available; rerun the CLI after replacing Node versions.
For future launches, open `~/Applications/Codex Tags.app` (pin it to the Dock). This small launcher invokes Loader with `loader.json`; Loader starts the official app with loopback debugging; it never monitors or restarts a running app. If a non-debuggable Codex is open, it shows a prompt to quit it manually. The official entry is unmodified. The Loader only maintains configured modules while the explicitly activated app runs; it does not relaunch Codex. Keep the activation Node installation available; rerun the CLI after replacing Node versions.

## Installed files

| Location | Contents |
| --- | --- |
| `~/Library/Application Support/Codex Sidebar Tags/` | Runtime, UI bundle, SQLite dependency, settings, index, logs |
| Its `plugin-marketplace/` directory | CLI-owned plugin snapshot and marketplace |
| `~/Applications/Codex Tags.app` | Small shell launcher, not a second Codex app |
| `~/Applications/Codex Tags.app` | Loader desktop entry; original path/identity retained for Dock compatibility |
| Codex plugin cache/data | Registered plugin payload and hook markers |

The signed app, authentication data and transcript files are never patched. Search/catalog reads stay local; only bounded snippets enter the injected UI. CDP remains a powerful trusted-local-machine capability.
Expand All @@ -30,8 +30,8 @@ The signed app, authentication data and transcript files are never patched. Sear

- **install / on / enable:** preflight → remove legacy supervisor → stop old controller → copy runtime/plugin → register → activate → verify.
- **update:** same flow using the invoked package version. Use `npx …@latest update` to fetch the newest; an old globally installed CLI cannot self-upgrade.
- **off / disable / restore:** stop controller and remove any legacy supervisor, restore UI and remove naming plugin; retain settings/index.
- **uninstall:** remove owned runtime, plugin registration, launcher, legacy supervisor, index and logs; retain settings.
- **off / disable / restore:** disable the Tags service/renderer module, remove any legacy supervisor and naming plugin; preserve other Loader modules; retain settings/index.
- **uninstall:** refuse when other modules share the installation; otherwise stop Loader and remove owned runtime, plugin registration, launcher, legacy supervisor, index and logs; retain settings.
- **uninstall --purge:** also remove settings, owned hook data and reachable renderer caches. Never deletes or renames Codex sessions.
- **status / doctor:** read-only. Doctor exits nonzero when not ready; a closed app or disabled installation is expected to be non-ready.

Expand Down Expand Up @@ -63,3 +63,7 @@ The first public release is an early release with pending manual acceptance expl
`prepublishOnly` runs source verification and package smoke. macOS CI checks Node 22/24 and bundle drift; it cannot replace GUI/hook acceptance. Use a configured trusted CI publisher if provenance is needed.

No open-source license is currently granted (`UNLICENSED`). Public distribution alone does not grant one; the owner must choose a license if open-source distribution is intended.

## Independent Loader package

`runtime/src/plugin-loader` is also a self-contained npm package, `@c0sc0s/codex-plugin-loader`, with no production dependencies. Run `npm pack ./runtime/src/plugin-loader` to produce its tarball. Its CLI is the explicit standalone startup entry; it does not install naming hooks, create Tags data or require the Tags controller. The Tags distribution embeds the same source, configures its renderer/service module and delegates the existing desktop entry directly to Loader. See [Loader contract](plugin-loader.md). The package exports the standalone desktop-launcher builder. Publication remains separate release work.
Loading
Loading