Skip to content
Draft
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
10 changes: 6 additions & 4 deletions .github/workflows/refit-codex.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
- "Cargo.toml"
- "crates/opsail/**"
- "crates/opsail-refit-codex/**"
- "crates/opsail-usage/**"
push:
branches:
- main
Expand All @@ -19,6 +20,7 @@ on:
- "Cargo.toml"
- "crates/opsail/**"
- "crates/opsail-refit-codex/**"
- "crates/opsail-usage/**"
workflow_dispatch:

permissions:
Expand Down Expand Up @@ -63,11 +65,11 @@ jobs:
rustup default "$rust_version"
rustup component add clippy

- name: Test refit and CLI
run: cargo test --locked --package opsail-refit-codex --package opsail
- name: Test refit, usage, and CLI
run: cargo test --locked --package opsail-refit-codex --package opsail-usage --package opsail

- name: Lint refit and CLI
run: cargo clippy --locked --package opsail-refit-codex --package opsail --all-targets --all-features -- -D warnings
- name: Lint refit, usage, and CLI
run: cargo clippy --locked --package opsail-refit-codex --package opsail-usage --package opsail --all-targets --all-features -- -D warnings

- name: Build native CLI
run: cargo build --locked --package opsail
Expand Down
5 changes: 4 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ crates/opsail Native CLI parsing, protocol routing, diagnostics, and ex
crates/opsail-chrome Chrome executable discovery, owned lifecycle, CDP, and DOM capture
crates/opsail-read Source orchestration, HTML acquisition, extraction, sanitization, and result schema
crates/opsail-refit-codex Codex refit lifecycle, target safety, and renderer integration
crates/opsail-usage Remaining-usage queries for supported CLI providers
packages/node Public `opsail` npm facade and native binary resolution
skills/bootstrap-opsail Transient agent-facing installation control plane
skills/opsail Unified Agent Skill for runtime Opsail capabilities
```

The native `opsail` crate owns the unified command entry point, while the public `opsail` npm package is a thin process adapter. Generated `@opsail/<platform>-<arch>` packages are implementation-only binary carriers, not additional APIs. `opsail-chrome` owns all Chrome-specific mechanics: cross-platform executable discovery, isolated process launch and cleanup, borrowed CDP connections, target lifecycle, navigation waits, and rendered DOM capture. It does not extract or sanitize content. `opsail-read` selects and validates sources, acquires non-browser HTML, delegates browser capture to `opsail-chrome`, and owns extraction, sanitization, and `ReadResult`. `opsail-refit-codex` owns the Codex-specific application identity, process and loopback CDP validation, renderer bridge, selectors, quota semantics, localization assets, and UI payload. Its refit lifecycle stays internal until a second adapter demonstrates a stable shared contract. A future action should become a sibling `opsail-<action>` crate once it has a cohesive typed API and independent tests, then be exposed through the existing CLI, npm facade, and unified runtime skill. Do not introduce a plugin ABI or shared framework before implemented modules demonstrate that need.
The native `opsail` crate owns the unified command entry point, while the public `opsail` npm package is a thin process adapter. Generated `@opsail/<platform>-<arch>` packages are implementation-only binary carriers, not additional APIs. `opsail-chrome` owns all Chrome-specific mechanics: cross-platform executable discovery, isolated process launch and cleanup, borrowed CDP connections, target lifecycle, navigation waits, and rendered DOM capture. It does not extract or sanitize content. `opsail-read` selects and validates sources, acquires non-browser HTML, delegates browser capture to `opsail-chrome`, and owns extraction, sanitization, and `ReadResult`. `opsail-refit-codex` owns the Codex-specific application identity, process and loopback CDP validation, renderer bridge, selectors, quota semantics, localization assets, and UI payload. Its refit lifecycle stays internal until a second adapter demonstrates a stable shared contract. `opsail-usage` owns read-only remaining-usage queries for supported CLI providers. Codex and Grok are the current adapters. It stays separate from renderer mutations in `opsail-refit-codex`. A future action should become a sibling `opsail-<action>` crate once it has a cohesive typed API and independent tests, then be exposed through the existing CLI, npm facade, and unified runtime skill. Do not introduce a plugin ABI or shared framework before implemented modules demonstrate that need.

## Library entry points

Expand All @@ -41,6 +42,8 @@ Borrowed CDP cleanup must close only Opsail-created targets. Detach and target c

Both `opsail-read` entry points return the versioned `ReadResult` model used by CLI JSON output. Browser captures retain distinct provenance: `SourceKind::Chrome` for owned launch and `SourceKind::Cdp` for a borrowed endpoint.

`opsail-usage` exposes `read_usage(&UsageOptions)`, returning the versioned `UsageReport` used by CLI JSON output. An empty provider list queries every registered provider; Codex and Grok are currently registered. Each adapter owns its discovery and query protocol. Reports must not expose credentials or raw provider responses. An unavailable provider does not block the remaining rows.

`opsail-refit-codex` exposes `CodexRefit`, configured through `CodexRefitConfig`, with asynchronous `enable_usage`, `disable_usage`, `status`, and read-only `doctor` operations. The adapter supports the validated macOS application at `/Applications/ChatGPT.app` and the current user's validated `OpenAI.Codex` Microsoft Store package on the Windows x64 and ARM64 release targets; Linux and 32-bit Windows releases are unsupported. Enable is attach-only unless its typed launch policy is explicitly `LaunchIfStopped`; that policy may start the application once through the platform's validated launch mechanism but must never quit, kill, restart, reload, modify, or re-sign it. `doctor`, `status`, and `disable` never launch. Connections must use only `127.0.0.1` and fail closed unless the platform application identity, process ownership, renderer URL and shell, sidebar, and expected local bridge all validate. Codex protocol names, selectors, quota semantics, localization JSON, and UI copy belong in this crate, not in a shared module.

## Development workflow
Expand Down
5 changes: 4 additions & 1 deletion CONTRIBUTING.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ crates/opsail 原生 CLI 解析、协议路由、诊断和退出行为
crates/opsail-chrome Chrome 可执行文件查找、自有生命周期、CDP 与 DOM 捕获
crates/opsail-read 来源编排、HTML 获取、正文提取、清洗和结果 schema
crates/opsail-refit-codex Codex refit 生命周期、目标安全校验与 renderer 集成
crates/opsail-usage 查询 CLI provider 剩余额度
packages/node 公开 `opsail` npm facade 与原生二进制解析
skills/bootstrap-opsail 面向 Agent 的临时安装控制面
skills/opsail 统一的 Opsail 运行时 Agent Skill
```

原生 `opsail` crate 负责统一命令入口,公开的 `opsail` npm package 是轻量进程适配层。生成的 `@opsail/<platform>-<arch>` package 仅承载二进制实现,不是额外 API。`opsail-chrome` 负责所有 Chrome 专属机制:跨平台可执行文件查找、隔离进程的启动与清理、借用 CDP 连接、target 生命周期、导航等待和渲染后 DOM 捕获;它不负责正文提取或清洗。`opsail-read` 选择并校验来源,获取非浏览器 HTML,将浏览器捕获委托给 `opsail-chrome`,并负责提取、清洗和 `ReadResult`。`opsail-refit-codex` 负责 Codex 专属的应用身份、进程与 loopback CDP 校验、renderer bridge、选择器、额度语义、本地化资源和 UI payload;在第二个适配器证明存在稳定共享契约前,refit 生命周期保持为其内部模块。未来行动在具备内聚的类型化 API 和独立测试后,应成为同级 `opsail-<action>` crate,再通过现有 CLI、npm facade 与统一运行时 Skill 暴露。在多个已实现模块证明有实际需要之前,不引入插件 ABI 或共享框架。
原生 `opsail` crate 负责统一命令入口,公开的 `opsail` npm package 是轻量进程适配层。生成的 `@opsail/<platform>-<arch>` package 仅承载二进制实现,不是额外 API。`opsail-chrome` 负责所有 Chrome 专属机制:跨平台可执行文件查找、隔离进程的启动与清理、借用 CDP 连接、target 生命周期、导航等待和渲染后 DOM 捕获;它不负责正文提取或清洗。`opsail-read` 选择并校验来源,获取非浏览器 HTML,将浏览器捕获委托给 `opsail-chrome`,并负责提取、清洗和 `ReadResult`。`opsail-refit-codex` 负责 Codex 专属的应用身份、进程与 loopback CDP 校验、renderer bridge、选择器、额度语义、本地化资源和 UI payload;在第二个适配器证明存在稳定共享契约前,refit 生命周期保持为其内部模块。`opsail-usage` 负责 CLI provider 的只读剩余额度查询,目前适配 Codex 和 Grok;它与 `opsail-refit-codex` 的 renderer 改造保持分离。未来行动在具备内聚的类型化 API 和独立测试后,应成为同级 `opsail-<action>` crate,再通过现有 CLI、npm facade 与统一运行时 Skill 暴露。在多个已实现模块证明有实际需要之前,不引入插件 ABI 或共享框架。

## 库入口

Expand All @@ -41,6 +42,8 @@ skills/opsail 统一的 Opsail 运行时 Agent Skill

`opsail-read` 的两个入口都返回 CLI JSON 输出所使用的带版本号 `ReadResult` 模型。浏览器捕获保留不同的来源信息:自有启动使用 `SourceKind::Chrome`,借用 endpoint 使用 `SourceKind::Cdp`。

`opsail-usage` 暴露 `read_usage(&UsageOptions)`,返回 CLI JSON 使用的带版本号 `UsageReport`。空 provider 列表查询所有已注册 provider,目前注册 Codex 和 Grok。各 adapter 自己负责发现与查询协议。报告不得暴露凭据或 provider 原始响应;单个 provider unavailable 不阻断其它结果。

`opsail-refit-codex` 暴露通过 `CodexRefitConfig` 配置的 `CodexRefit`,提供异步的 `enable_usage`、`disable_usage`、`status` 与只读 `doctor` 操作。适配器支持经过校验的 macOS 应用 `/Applications/ChatGPT.app`,以及 Windows x64 和 ARM64 发布目标上当前用户已校验的 `OpenAI.Codex` Microsoft Store 包;Linux 和 32 位 Windows 发布不受支持。Enable 默认为只附加;只有显式使用类型化 `LaunchIfStopped` 策略时,才可通过平台校验后的启动机制启动一次应用,但不得退出、kill、重启、重载、修改或重新签名它。`doctor`、`status` 与 `disable` 绝不启动应用。连接必须只使用 `127.0.0.1`,并且只有平台应用身份、进程归属、renderer URL 与 shell、侧栏以及预期本机 bridge 全部通过校验后才能继续。Codex 协议名、选择器、额度语义、本地化 JSON 和 UI 文案均属于此 crate,不进入共享模块。

## 开发流程
Expand Down
19 changes: 18 additions & 1 deletion Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"crates/opsail-chrome",
"crates/opsail-read",
"crates/opsail-refit-codex",
"crates/opsail-usage",
]
resolver = "3"

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ opsail read https://example.com/app --launch

See [`opsail-read`](https://github.com/lencx/opsail/blob/main/crates/opsail-read/README.md) for acquisition, extraction, result contracts, and Rust APIs. See [`opsail-chrome`](https://github.com/lencx/opsail/blob/main/crates/opsail-chrome/README.md) for Chrome discovery, owned launch, borrowed CDP, navigation, and rendered DOM capture.

### Query remaining usage

`opsail usage` reports remaining windows from supported CLI providers. Codex and Grok are currently supported. Pass a provider name to query only that provider.

### Refit Codex

`opsail refit codex` provides a reversible, target-validated Codex adapter. Its first feature adds localized remaining-usage information to the Codex sidebar using the renderer's existing local bridge, without model calls or changes to the application bundle.
Expand All @@ -60,6 +64,7 @@ See [`opsail-refit-codex`](https://github.com/lencx/opsail/blob/main/crates/opsa
| [`opsail`](https://crates.io/crates/opsail) | Native CLI and unified command entry point | Run `opsail --help` |
| [`opsail-read`](https://crates.io/crates/opsail-read) | Content acquisition, extraction, sanitization, and result contracts | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-read/README.md) |
| [`opsail-chrome`](https://crates.io/crates/opsail-chrome) | Cross-platform Chrome lifecycle, CDP transport, and rendered capture | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-chrome/README.md) |
| [`opsail-usage`](https://crates.io/crates/opsail-usage) | Remaining-usage queries for supported CLI providers | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-usage/README.md) |
| [`opsail-refit-codex`](https://crates.io/crates/opsail-refit-codex) | Validated Codex refit lifecycle, usage semantics, localization, and UI payload | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-refit-codex/README.md) |
| [`opsail`](https://www.npmjs.com/package/opsail) for Node.js | ESM API and native binary distribution | [README](https://github.com/lencx/opsail/blob/main/packages/node/README.md) |

Expand Down
5 changes: 5 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ opsail read https://example.com/app --launch

内容获取、正文提取、结果契约和 Rust API 请参阅 [`opsail-read`](https://github.com/lencx/opsail/blob/main/crates/opsail-read/README.md);Chrome 发现、自有启动、借用 CDP、页面导航和渲染 DOM 捕获请参阅 [`opsail-chrome`](https://github.com/lencx/opsail/blob/main/crates/opsail-chrome/README.md)。

### 查询用量

`opsail usage` 查询当前支持的 CLI provider 剩余额度。目前支持 Codex 和 Grok;传入 provider 名称时只查询对应 provider。

### Codex Refit

`opsail refit codex` 提供可逆且经过目标校验的 Codex 适配器。它的首个功能通过 renderer 已有的本地 bridge,在 Codex 左侧栏显示本地化的剩余额度信息,不调用模型,也不修改应用包。
Expand All @@ -60,6 +64,7 @@ opsail refit codex enable usage --launch
| [`opsail`](https://crates.io/crates/opsail) | 原生 CLI 与统一命令入口 | 运行 `opsail --help` |
| [`opsail-read`](https://crates.io/crates/opsail-read) | 内容获取、正文提取、清理和结果契约 | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-read/README.md) |
| [`opsail-chrome`](https://crates.io/crates/opsail-chrome) | 跨平台 Chrome 生命周期、CDP 传输和渲染捕获 | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-chrome/README.md) |
| [`opsail-usage`](https://crates.io/crates/opsail-usage) | CLI provider 剩余额度查询 | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-usage/README.md) |
| [`opsail-refit-codex`](https://crates.io/crates/opsail-refit-codex) | Codex 适配生命周期、额度语义、多语言和 UI payload | [README](https://github.com/lencx/opsail/blob/main/crates/opsail-refit-codex/README.md) |
| Node.js [`opsail`](https://www.npmjs.com/package/opsail) | ESM API 与原生二进制分发 | [README](https://github.com/lencx/opsail/blob/main/packages/node/README.md) |

Expand Down
30 changes: 30 additions & 0 deletions crates/opsail-usage/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "opsail-usage"
description = "Codex and Grok CLI remaining-usage queries for Opsail"
version = "0.1.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
authors.workspace = true
readme = "README.md"
repository.workspace = true

[dependencies]
futures-util.workspace = true
reqwest.workspace = true
rustls.workspace = true
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio.workspace = true

[target.'cfg(windows)'.dependencies]
process-wrap.workspace = true

[dev-dependencies]
tempfile.workspace = true
tokio.workspace = true
wiremock.workspace = true

[lints]
workspace = true
54 changes: 54 additions & 0 deletions crates/opsail-usage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# opsail-usage

`opsail-usage` is the Rust library behind
[`opsail usage`](https://github.com/lencx/opsail#query-remaining-usage). It
queries remaining-usage windows from supported CLI providers and returns a
versioned, credential-free report. Codex and Grok are currently supported.

This crate does not attach to ChatGPT.app, inject renderer UI, or share code
with `opsail-refit-codex`. Sidebar display remains a refit concern.

## Capabilities

- Query all current providers or one named provider.
- Resolve and query each provider through its own adapter.
- Return ready and unavailable rows without blocking other providers.
- Keep credentials and raw provider responses out of reports and diagnostics.

The current Codex adapter uses a short-lived `codex app-server`. The current
Grok adapter uses the CLI auth file and the official grok.com billing endpoint.

## Installation

```toml
[dependencies]
opsail-usage = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
```

## Query remaining windows

```rust
use opsail_usage::{UsageOptions, read_usage};

#[tokio::main]
async fn main() {
let report = read_usage(&UsageOptions::default()).await;
for entry in report.providers {
println!("{}", entry.provider.as_str());
}
}
```

The default deadline is 15 seconds.

## Result contract

Reports use `schemaVersion: 1` and a `providers` array. Each row has
`provider` and `status` (`ready` or `unavailable`). Optional numeric fields are
omitted when unused. The library never returns raw RPC frames or auth material.

## Trust boundary

Callers pass file paths, not secrets. Opsail does not log in, refresh tokens, or
print credentials. A missing or unsigned-in provider becomes an unavailable row.
Loading