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 .github/AUTHOR_MAP
Original file line number Diff line number Diff line change
Expand Up @@ -263,3 +263,6 @@ Arlo = Arlo <253300483+xiaoray-blip@users.noreply.github.com>
xyzs996 = Mark Yan <396284+xyzs996@users.noreply.github.com>
xyzs996@gmail.com = Mark Yan <396284+xyzs996@users.noreply.github.com>
Mark Yan = Mark Yan <396284+xyzs996@users.noreply.github.com>
henrikdukefoss = Henrik Bakke Dukefoss <76407537+henrikdukefoss@users.noreply.github.com>
Henrik Bakke Dukefoss = Henrik Bakke Dukefoss <76407537+henrikdukefoss@users.noreply.github.com>
henrikdukef@gmail.com = Henrik Bakke Dukefoss <76407537+henrikdukefoss@users.noreply.github.com>
6 changes: 4 additions & 2 deletions .github/workflows/ohos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:
- 'crates/**'
- 'scripts/ohos/**'
- 'scripts/ohos-env.sh'
- 'scripts/ohos-env.ps1'
- 'scripts/release/check-ohos-deps.sh'
pull_request:
branches: [main, master]
Expand All @@ -20,6 +21,7 @@ on:
- 'crates/**'
- 'scripts/ohos/**'
- 'scripts/ohos-env.sh'
- 'scripts/ohos-env.ps1'
- 'scripts/release/check-ohos-deps.sh'
schedule:
- cron: '47 7 * * 1'
Expand Down Expand Up @@ -66,7 +68,7 @@ jobs:
# (checksum-verified from the GitHub mirror below).
cache: ${{ github.ref == 'refs/heads/main' }}
mirror: true
- name: Check Codewhale TUI with the real SDK/sysroot
- name: Check Codewhale TUI and CLI with the real SDK/sysroot
shell: bash
env:
OHOS_NATIVE_SDK: ${{ steps.ohos-sdk.outputs.ohos_sdk_native }}
Expand All @@ -75,4 +77,4 @@ jobs:
test -x "${OHOS_NATIVE_SDK}/llvm/bin/clang"
test -d "${OHOS_NATIVE_SDK}/sysroot"
. ./scripts/ohos-env.sh
cargo check --target aarch64-unknown-linux-ohos -p codewhale-tui --locked
cargo check --target aarch64-unknown-linux-ohos -p codewhale-tui -p codewhale-cli --locked
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.10.2] - 2026-10-10

- OpenHarmony builds on Windows select Ninja, with an SDK-bundled fallback and
a clear error when it is unavailable. The CLI keeps its download and Work
context integrity checks available on OHOS by sharing an ungated SHA-256
helper (thanks @shenjackyuanjie, #6989; reported in #6988).

- Desktop and HTTP app-server turns feed an existing shared pet companion when
its followed session has no live producer. Automatic selection is checked
atomically by the owner; a live TUI keeps its source. The Runtime producer
Expand Down Expand Up @@ -473,6 +478,8 @@ launcher guidance.

### Contributors

- **[@henrikdukefoss](https://github.com/henrikdukefoss)** — added the Standard Compute compatible-host descriptor, with live model discovery and its billing basis kept Unknown ([#6985](https://github.com/codewhale-hq/Codewhale/pull/6985)).
- **[@shenjackyuanjie](https://github.com/shenjackyuanjie)** — diagnosed and fixed the Windows OpenHarmony CMake generator and CLI hashing-module build failures ([#6988](https://github.com/codewhale-hq/Codewhale/issues/6988), [#6989](https://github.com/codewhale-hq/Codewhale/pull/6989)).
- **[@LIghtJUNction](https://github.com/LIghtJUNction)** — plugin shell install and in-TUI plugin OAuth sign-in ([#6950](https://github.com/codewhale-hq/Codewhale/pull/6950)).
- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a relocated Codewhale home ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)).
- **[@dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)).
Expand Down
4 changes: 2 additions & 2 deletions crates/cli/src/cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1498,7 +1498,7 @@ impl<'a, T: CloudTransport> CloudClient<'a, T> {
return Err(response_error(&response));
}
if response.body.len() as u64 != metadata.size
|| crate::update::sha256_hex(&response.body) != metadata.sha256
|| crate::hashing::sha256_hex(&response.body) != metadata.sha256
{
bail!(
"The downloaded file did not match its saved size and SHA-256; nothing was written"
Expand Down Expand Up @@ -1959,7 +1959,7 @@ fn dispatch_stamp(objective: &str) -> String {
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |elapsed| elapsed.as_nanos());
let seed = format!("{nanos}:{}:{objective}", std::process::id());
crate::update::sha256_hex(seed.as_bytes())[..24].to_string()
crate::hashing::sha256_hex(seed.as_bytes())[..24].to_string()
}

pub(crate) enum AccountDispatch {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/cloud/work.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ pub(super) fn assign<T: CloudTransport, W: Write>(
bail!("The Codewhale service returned Work for a different Agent");
}
if let Some(context) = task_context {
let digest = crate::update::sha256_hex(&serde_json::to_vec(context)?);
let digest = crate::hashing::sha256_hex(&serde_json::to_vec(context)?);
if item.task_context_digest != digest {
return Err(CloudTransportError::new("The Work reply does not confirm the requested source context; replay the same message and context", std::io::Error::other("task context mismatch")).into());
}
Expand Down
16 changes: 16 additions & 0 deletions crates/cli/src/hashing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// Compute the SHA256 hex digest of data.
pub(crate) fn sha256_hex(data: &[u8]) -> String {
use sha2::Digest;
let hash = sha2::Sha256::digest(data);
hex_bytes(hash)
}

fn hex_bytes(bytes: impl AsRef<[u8]>) -> String {
let bytes = bytes.as_ref();
let mut out = String::with_capacity(bytes.len() * 2);
for byte in bytes {
use std::fmt::Write as _;
let _ = write!(&mut out, "{byte:02x}");
}
out
}
1 change: 1 addition & 0 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod cloud;
mod config_bundles;
mod credential_handoff;
mod dispatch;
mod hashing;
mod metrics;
#[cfg(not(target_env = "ohos"))]
mod update;
Expand Down
19 changes: 2 additions & 17 deletions crates/cli/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ use std::io::Write;
use std::sync::Arc;
use std::time::Duration;

use crate::hashing::sha256_hex;

mod compiled_host;

const GITHUB_LATEST_RELEASE_PAGE_URL: &str =
Expand Down Expand Up @@ -1841,23 +1843,6 @@ fn download_url_once_with(
Ok((status, bytes))
}

/// Compute the SHA256 hex digest of data.
pub(crate) fn sha256_hex(data: &[u8]) -> String {
use sha2::Digest;
let hash = sha2::Sha256::digest(data);
hex_bytes(hash)
}

fn hex_bytes(bytes: impl AsRef<[u8]>) -> String {
let bytes = bytes.as_ref();
let mut out = String::with_capacity(bytes.len() * 2);
for byte in bytes {
use std::fmt::Write as _;
let _ = write!(&mut out, "{byte:02x}");
}
out
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct GlibcVersion {
major: u32,
Expand Down
15 changes: 15 additions & 0 deletions crates/config/assets/provider_descriptors.json
Original file line number Diff line number Diff line change
Expand Up @@ -2227,6 +2227,21 @@
"meituan-sensenova-cn"
]
},
{
"id": "standardcompute",
"label": "Standard Compute",
"wire": "openai-compatible",
"base_url": "https://api.stdcmpt.com/v1",
"api_key_env": "STANDARDCOMPUTE_API_KEY",
"default_model": "standardcompute",
"discovery": "models_endpoint",
"docs_url": "https://docs.standardcompute.com/quickstart",
"credential_url": "https://standardcompute.com/dashboard/api-keys",
"guidance": "OpenAI-compatible host (Chat Completions + GET /v1/models). The standardcompute model id routes each request across models and providers; vendor-prefixed ids such as deepseek/deepseek-v4-pro pin one model. Live /v1/models is the model authority. Store STANDARDCOMPUTE_API_KEY, not a raw key.",
"aliases": [
"standard-compute"
]
},
{
"id": "tsubasa",
"label": "Tsubasa",
Expand Down
16 changes: 15 additions & 1 deletion crates/config/src/descriptors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,20 @@ mod tests {
);
}

#[test]
fn standard_compute_is_a_descriptor_row() {
let row = provider_descriptor("standard-compute").expect("standardcompute");
assert_eq!(row.id, "standardcompute");
assert_eq!(row.wire, DescriptorWire::OpenaiCompatible);
assert_eq!(row.base_url, "https://api.stdcmpt.com/v1");
assert_eq!(row.api_key_env, "STANDARDCOMPUTE_API_KEY");
assert_eq!(row.default_model, "standardcompute");
assert_eq!(
provider_descriptor("standard_compute").map(|row| row.id.as_str()),
Some("standardcompute")
);
}

#[test]
fn descriptors_do_not_embed_model_rosters() {
let raw = DESCRIPTORS_JSON;
Expand Down Expand Up @@ -404,6 +418,6 @@ mod tests {
// reusing its name; compatible-host lookup remains separately scoped.
assert!(provider_descriptor("openai").is_none());
assert!(provider_descriptor("deepseek").is_none());
assert_eq!(bundled_provider_descriptors().len(), 10);
assert_eq!(bundled_provider_descriptors().len(), 11);
}
}
7 changes: 7 additions & 0 deletions crates/tui/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [0.10.2] - 2026-10-10

- OpenHarmony builds on Windows select Ninja, with an SDK-bundled fallback and
a clear error when it is unavailable. The CLI keeps its download and Work
context integrity checks available on OHOS by sharing an ungated SHA-256
helper (thanks @shenjackyuanjie, #6989; reported in #6988).

- Desktop and HTTP app-server turns feed an existing shared pet companion when
its followed session has no live producer. Automatic selection is checked
atomically by the owner; a live TUI keeps its source. The Runtime producer
Expand Down Expand Up @@ -473,6 +478,8 @@ launcher guidance.

### Contributors

- **[@henrikdukefoss](https://github.com/henrikdukefoss)** — added the Standard Compute compatible-host descriptor, with live model discovery and its billing basis kept Unknown ([#6985](https://github.com/codewhale-hq/Codewhale/pull/6985)).
- **[@shenjackyuanjie](https://github.com/shenjackyuanjie)** — diagnosed and fixed the Windows OpenHarmony CMake generator and CLI hashing-module build failures ([#6988](https://github.com/codewhale-hq/Codewhale/issues/6988), [#6989](https://github.com/codewhale-hq/Codewhale/pull/6989)).
- **[@LIghtJUNction](https://github.com/LIghtJUNction)** — plugin shell install and in-TUI plugin OAuth sign-in ([#6950](https://github.com/codewhale-hq/Codewhale/pull/6950)).
- **[@SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a relocated Codewhale home ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)).
- **[@dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)).
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/src/pricing/default_coverage_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ fn shipped_default_routes_have_reviewed_pricing_coverage() {
assert_eq!(built_in_count, 51, "review changed shipped-default breadth");
assert_eq!(
defaults.len() - built_in_count,
10,
11,
"review compatible-default breadth"
);
let recorded_at = Utc.with_ymd_and_hms(2026, 9, 8, 12, 0, 0).unwrap();
Expand Down
3 changes: 2 additions & 1 deletion crates/tui/src/pricing/default_coverage_unpriced.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
},
{
"reason": "unknown_billing_basis",
"review": "No mode/credential product is supplied. Both MiniMax dialects share a PAYG/plan endpoint that cannot prove product (route_billing.rs:253,402; pricing.rs:534); Ollama Cloud is unclassified possible spend, not local/free (pricing.rs:411). Generic Custom and nine non-Baseten compatible defaults are transport descriptors without a reviewed product; production envelope refuses Unknown at cost_status.rs:239. AICraft is the sixth compatible default: dc170add3 restored its row from the closed PR #6171 with endpoint, credential env and bootstrap model only, and no billing product or pricing row was reviewed for it. Its default is claude-4.6-sonnet, which has no bundled/hand row either; that omission is recorded here on the shape of the descriptor, and a real source review of AICraft's billing basis is still owed. Tsubasa (#6695) is the seventh: endpoint, credential env and bootstrap model tsubasa-pro only; its public site lists prepaid-credit per-token rates, but no pricing row was reviewed or frozen for it, so it stays Unknown here too. Yolo-Auto is the eighth: its row carries endpoint, credential env and bootstrap model (qwen3.8-flash, the vendor's recommended start model on free and paid plans) only, the gateway sells flat-rate subscription tiers with no published per-token catalog to review, and the URL proves no plan product, so the route stays Unknown rather than being guessed into subscription or local-no-bill; a source review of Yolo-Auto's billing basis is owed. Cheaper Inference is the ninth non-Baseten compatible default: its gpt-5.4-mini bootstrap model and API endpoint do not establish a billing product or reviewed price, so it also remains Unknown. ChatGPT source review: the official API endpoint alone does not prove a ChatGPT plan product. route_billing.rs capture_product requires the Config's owned official registration for Subscription; the offline default inventory has none and remains Unknown. The retired backend endpoint supplies no transport or billing authority.",
"review": "No mode/credential product is supplied. Both MiniMax dialects share a PAYG/plan endpoint that cannot prove product (route_billing.rs:253,402; pricing.rs:534); Ollama Cloud is unclassified possible spend, not local/free (pricing.rs:411). Generic Custom and ten non-Baseten compatible defaults are transport descriptors without a reviewed product; production envelope refuses Unknown at cost_status.rs:239. AICraft is the sixth compatible default: dc170add3 restored its row from the closed PR #6171 with endpoint, credential env and bootstrap model only, and no billing product or pricing row was reviewed for it. Its default is claude-4.6-sonnet, which has no bundled/hand row either; that omission is recorded here on the shape of the descriptor, and a real source review of AICraft's billing basis is still owed. Tsubasa (#6695) is the seventh: endpoint, credential env and bootstrap model tsubasa-pro only; its public site lists prepaid-credit per-token rates, but no pricing row was reviewed or frozen for it, so it stays Unknown here too. Yolo-Auto is the eighth: its row carries endpoint, credential env and bootstrap model (qwen3.8-flash, the vendor's recommended start model on free and paid plans) only, the gateway sells flat-rate subscription tiers with no published per-token catalog to review, and the URL proves no plan product, so the route stays Unknown rather than being guessed into subscription or local-no-bill; a source review of Yolo-Auto's billing basis is owed. Cheaper Inference is the ninth non-Baseten compatible default: its gpt-5.4-mini bootstrap model and API endpoint do not establish a billing product or reviewed price, so it also remains Unknown. Standard Compute is the tenth: its row carries endpoint, credential env and the standardcompute router id only; the gateway sells flat monthly plans with a per-plan compute budget, and neither the URL nor the router id proves a plan product or per-request price, so the route stays Unknown. ChatGPT source review: the official API endpoint alone does not prove a ChatGPT plan product. route_billing.rs capture_product requires the Config's owned official registration for Subscription; the offline default inventory has none and remains Unknown. The retired backend endpoint supplies no transport or billing authority.",
"routes": [
["aicraft", "claude-4.6-sonnet", "https://aicraftapi.com/v1", "chat_completions", "unknown"],
["cerebras", "llama-3.3-70b", "https://api.cerebras.ai/v1", "chat_completions", "unknown"],
Expand All @@ -50,6 +50,7 @@
["ollama-cloud", "gpt-oss:120b", "https://ollama.com/v1", "chat_completions", "unknown"],
["openai-codex", "gpt-5.6", "https://api.openai.com/v1", "responses", "unknown"],
["sensenova", "deepseek-v4-flash", "https://token.sensenova.cn/v1", "chat_completions", "unknown"],
["standardcompute", "standardcompute", "https://api.stdcmpt.com/v1", "chat_completions", "unknown"],
["tsubasa", "tsubasa-pro", "https://api.tsubasa.sh/v1", "chat_completions", "unknown"],
["yolo-auto", "qwen3.8-flash", "https://yolo-auto.com/v1", "chat_completions", "unknown"]
]
Expand Down
2 changes: 2 additions & 0 deletions docs/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ changelog or release notes, and this contributor record.

**Contributions**

- **[Henrik Bakke Dukefoss / henrikdukefoss](https://github.com/henrikdukefoss)** — added the Standard Compute compatible-host descriptor, with live model discovery and its billing basis kept Unknown ([#6985](https://github.com/codewhale-hq/Codewhale/pull/6985)).
- **[shenjackyuanjie](https://github.com/shenjackyuanjie)** — diagnosed and fixed the Windows OpenHarmony CMake generator and CLI hashing-module build failures ([#6988](https://github.com/codewhale-hq/Codewhale/issues/6988), [#6989](https://github.com/codewhale-hq/Codewhale/pull/6989)).
- **[Sh1Zuku / SparkofSpike](https://github.com/SparkofSpike)** — live network-policy updates and goal milestone hand-back ([#6928](https://github.com/codewhale-hq/Codewhale/pull/6928), [#6930](https://github.com/codewhale-hq/Codewhale/pull/6930)), and a Codewhale home reached through a symlink or junction ([#6947](https://github.com/codewhale-hq/Codewhale/issues/6947)).
- **[dajiaohuang](https://github.com/dajiaohuang)** — cache-write token accounting, with missing and explicit-zero usage preserved ([#6913](https://github.com/codewhale-hq/Codewhale/pull/6913)).
- **[gaord](https://github.com/gaord)** — embedder-declared telemetry surface support without changing collection policy ([#6916](https://github.com/codewhale-hq/Codewhale/pull/6916)).
Expand Down
10 changes: 10 additions & 0 deletions docs/PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ against the vendor's own docs before trusting any value here:
| Tsubasa | `https://api.tsubasa.sh/v1` | `tsubasa-pro`, `tsubasa-fast` (32,768-token context) | `TSUBASA_API_KEY` |
| Cheaper Inference | `https://api.cheaperinference.com/v1` | `gpt-5.4-mini`, `claude-sonnet-5`, `gemini-3.1-pro` | `CHEAPER_INFERENCE_API_KEY` |
| Yolo-Auto | `https://yolo-auto.com/v1` | `qwen3.8-flash`; `yolo` / `yolo-small` | `YOLO_AUTO_API_KEY` |
| Standard Compute | `https://api.stdcmpt.com/v1` | `standardcompute` (router); `deepseek/deepseek-v4-pro`, `anthropic/claude-sonnet-5.5` | `STANDARDCOMPUTE_API_KEY` |

AICraft's roster spans DeepSeek, Anthropic Claude, Google Gemini, Qwen, GLM,
MiniMax and Doubao ids on its OpenAI-compatible endpoint. The authority is
Expand All @@ -65,6 +66,15 @@ Pricing varies by model and route; consult the provider’s current catalog.
The authority is `GET https://api.cheaperinference.com/v1/models` with your key.
Docs: <https://cheaperinference.com/docs>.

Standard Compute offers flat monthly plans with a compute budget. By default,
requests return HTTP 402 when it is used; eligible plans can enable Pay as you go
to continue with additional prepaid charges. See its
[budget policy](https://docs.standardcompute.com/budgets-and-limits).
The `standardcompute` model id routes each request across models and providers;
vendor-prefixed ids such as `deepseek/deepseek-v4-pro` pin one model. The
authority is `GET https://api.stdcmpt.com/v1/models`.
Docs: <https://docs.standardcompute.com/quickstart>.

OpenCode Zen and OpenCode Go are first-class provider routes, configured like
any other provider below; they are not part of this table. In `/provider`,
type to filter the list (letters not bound to a row action); `Ctrl+T` probes the
Expand Down
Loading
Loading