Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ jobs:
echo "release_channel=stable" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "tag=v$version" >> "$GITHUB_OUTPUT"
echo "name=TritonAI Code v$version" >> "$GITHUB_OUTPUT"
echo "name=TritonAI Harness v$version" >> "$GITHUB_OUTPUT"
echo "cli_dist_tag=latest" >> "$GITHUB_OUTPUT"
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "is_prerelease=false" >> "$GITHUB_OUTPUT"
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# T3 Code
# TritonAI Harness

T3 Code is a minimal web GUI for coding agents (currently Codex, Claude, Cursor, and OpenCode, more coming soon).
TritonAI Harness is a minimal web GUI for coding agents (currently Codex, Claude, Cursor, and OpenCode, more coming soon).

## Installation

> [!WARNING]
> T3 Code currently supports Codex, Claude, Cursor, and OpenCode.
> TritonAI Harness currently supports Codex, Claude, Cursor, and OpenCode.
> Install and authenticate at least one provider before use:
>
> - Codex: install [Codex CLI](https://developers.openai.com/codex/cli) and run `codex login`
Expand Down Expand Up @@ -63,7 +63,7 @@ There's no public docs site yet, checkout the miscellaneous markdown files in [d

### Install `vp`

T3 Code uses Vite+ so you'll need to install the global `vp` command-line tool.
TritonAI Harness uses Vite+ so you'll need to install the global `vp` command-line tool.

#### macOS / Linux

Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
"tailwindcss": "^4.0.0",
"vite-plus": "catalog:"
},
"productName": "TritonAI Code"
"productName": "TritonAI Harness"
}
4 changes: 2 additions & 2 deletions apps/desktop/scripts/electron-launcher.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// This file mostly exists because we want dev mode to say "TritonAI Code (Dev)" instead of "electron"
// This file mostly exists because we want dev mode to say "TritonAI Harness (Dev)" instead of "electron"

import { spawnSync } from "node:child_process";
import {
Expand Down Expand Up @@ -26,7 +26,7 @@ const repoRoot = resolve(desktopDir, "..", "..");
const devBundleIdSuffix = basename(repoRoot)
.toLowerCase()
.replaceAll(/[^a-z0-9]+/g, "");
export const APP_DISPLAY_NAME = isDevelopment ? "TritonAI Code (Dev)" : "TritonAI Code";
export const APP_DISPLAY_NAME = isDevelopment ? "TritonAI Harness (Dev)" : "TritonAI Harness";
export const APP_BUNDLE_ID = isDevelopment
? `edu.ucsd.ai.tritonai-code.dev.${devBundleIdSuffix || "local"}`
: "edu.ucsd.ai.tritonai-code";
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/app/DesktopApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const handleFatalStartupError = Effect.fn("desktop.startup.handleFatalStartupErr
const wasQuitting = yield* Ref.getAndSet(state.quitting, true);
if (!wasQuitting) {
yield* electronDialog.showErrorBox(
"TritonAI Code failed to start",
"TritonAI Harness failed to start",
`Stage: ${stage}\n${message}${detail}`,
);
}
Expand Down
29 changes: 22 additions & 7 deletions apps/desktop/src/app/DesktopAppIdentity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const defaultEnvironmentInput = {
platform: "darwin",
processArch: "arm64",
appVersion: "1.2.3",
appPath: "/Applications/TritonAI Code.app/Contents/Resources/app.asar",
appPath: "/Applications/TritonAI Harness.app/Contents/Resources/app.asar",
isPackaged: true,
resourcesPath: "/Applications/TritonAI Code.app/Contents/Resources",
resourcesPath: "/Applications/TritonAI Harness.app/Contents/Resources",
runningUnderArm64Translation: false,
} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput;

Expand All @@ -38,7 +38,7 @@ interface ElectronAppCalls {
const makeElectronAppLayer = (calls: ElectronAppCalls) =>
Layer.succeed(ElectronApp.ElectronApp, {
metadata: Effect.die("unexpected metadata read"),
name: Effect.succeed("TritonAI Code"),
name: Effect.succeed("TritonAI Harness"),
whenReady: Effect.void,
quit: Effect.void,
exit: () => Effect.void,
Expand Down Expand Up @@ -105,6 +105,7 @@ const withIdentity = <A, E, R>(
readonly calls?: ElectronAppCalls;
readonly environment?: TestEnvironmentInput;
readonly legacyPathExists?: boolean;
readonly legacyPathNeedle?: string;
readonly packageJson?: string;
readonly pngIconPath?: Option.Option<string>;
} = {},
Expand All @@ -121,7 +122,10 @@ const withIdentity = <A, E, R>(
Layer.provideMerge(
FileSystem.layerNoop({
exists: (path) =>
Effect.succeed(input.legacyPathExists === true && path.includes("T3 Code")),
Effect.succeed(
input.legacyPathExists === true &&
path.includes(input.legacyPathNeedle ?? "T3 Code"),
),
readFileString: () =>
Effect.succeed(input.packageJson ?? '{"t3codeCommitHash":"abcdef1234567890"}'),
}),
Expand All @@ -136,14 +140,25 @@ const withIdentity = <A, E, R>(

describe("DesktopAppIdentity", () => {
it.effect("uses the downstream userData path for packaged builds", () =>
withIdentity(
Effect.gen(function* () {
const identity = yield* DesktopAppIdentity.DesktopAppIdentity;
const userDataPath = yield* identity.resolveUserDataPath;

assert.equal(userDataPath, "/Users/alice/Library/Application Support/TritonAI Harness");
}),
),
);

it.effect("keeps using the legacy TritonAI Code userData path when it already exists", () =>
withIdentity(
Effect.gen(function* () {
const identity = yield* DesktopAppIdentity.DesktopAppIdentity;
const userDataPath = yield* identity.resolveUserDataPath;

assert.equal(userDataPath, "/Users/alice/Library/Application Support/TritonAI Code");
}),
{ legacyPathExists: true },
{ legacyPathExists: true, legacyPathNeedle: "TritonAI Code" },
),
);

Expand Down Expand Up @@ -179,8 +194,8 @@ describe("DesktopAppIdentity", () => {
const identity = yield* DesktopAppIdentity.DesktopAppIdentity;
yield* identity.configure;

assert.deepEqual(calls.setName, ["TritonAI Code"]);
assert.equal(calls.setAboutPanelOptions[0]?.applicationName, "TritonAI Code");
assert.deepEqual(calls.setName, ["TritonAI Harness"]);
assert.equal(calls.setAboutPanelOptions[0]?.applicationName, "TritonAI Harness");
assert.equal(calls.setAboutPanelOptions[0]?.applicationVersion, "1.2.3");
assert.equal(calls.setAboutPanelOptions[0]?.version, "0123456789ab");
assert.deepEqual(calls.setDockIcon, ["/icon.png"]);
Expand Down
4 changes: 2 additions & 2 deletions apps/desktop/src/app/DesktopEnvironment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ const defaultInput = {
platform: "darwin",
processArch: "arm64",
appVersion: "0.0.22",
appPath: "/Applications/TritonAI Code.app/Contents/Resources/app.asar",
appPath: "/Applications/TritonAI Harness.app/Contents/Resources/app.asar",
isPackaged: false,
resourcesPath: "/Applications/TritonAI Code.app/Contents/Resources",
resourcesPath: "/Applications/TritonAI Harness.app/Contents/Resources",
runningUnderArm64Translation: false,
} satisfies DesktopEnvironment.MakeDesktopEnvironmentInput;

Expand Down
6 changes: 3 additions & 3 deletions apps/desktop/src/app/DesktopEnvironment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class DesktopEnvironment extends Context.Service<
DesktopEnvironmentShape
>()("@t3tools/desktop/app/DesktopEnvironment") {}

const APP_BASE_NAME = "TritonAI Code";
const APP_BASE_NAME = "TritonAI Harness";

function resolveDesktopAppStageLabel(input: {
readonly isDevelopment: boolean;
Expand Down Expand Up @@ -162,8 +162,8 @@ const makeDesktopEnvironment = Effect.fn("desktop.environment.make")(function* (
});
const displayName = branding.displayName;
const stateDir = path.join(baseDir, isDevelopment ? "dev" : "userdata");
const userDataDirName = isDevelopment ? "tritonai-code-dev" : "TritonAI Code";
const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : null;
const userDataDirName = isDevelopment ? "tritonai-code-dev" : "TritonAI Harness";
const legacyUserDataDirName = isDevelopment ? "T3 Code (Dev)" : "TritonAI Code";
const resourcesPath = input.resourcesPath;

return DesktopEnvironment.of({
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/electron/ElectronApp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ vi.mock("electron", () => ({
getVersion: getVersionMock,
isDefaultProtocolClient: isDefaultProtocolClientMock,
isPackaged: true,
name: "TritonAI Code",
name: "TritonAI Harness",
on: onMock,
quit: quitMock,
relaunch: relaunchMock,
Expand Down
3 changes: 2 additions & 1 deletion apps/desktop/src/ssh/DesktopSshPasswordPrompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import * as IpcChannels from "../ipc/channels.ts";
import * as ElectronWindow from "../electron/ElectronWindow.ts";

const DEFAULT_SSH_PASSWORD_PROMPT_TIMEOUT_MS = 3 * 60 * 1000;
const WINDOW_UNAVAILABLE_MESSAGE = "TritonAI Code window is not available for SSH authentication.";
const WINDOW_UNAVAILABLE_MESSAGE =
"TritonAI Harness window is not available for SSH authentication.";

type DesktopSshPasswordPromptResolutionInput =
typeof DesktopSshPasswordPromptResolutionInputSchema.Type;
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/window/DesktopApplicationMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const environmentInput = {

const electronAppLayer = Layer.succeed(ElectronApp.ElectronApp, {
metadata: Effect.die("unexpected metadata read"),
name: Effect.succeed("TritonAI Code"),
name: Effect.succeed("TritonAI Harness"),
whenReady: Effect.void,
quit: Effect.void,
exit: () => Effect.void,
Expand Down
2 changes: 1 addition & 1 deletion apps/desktop/src/window/DesktopApplicationMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const checkForUpdatesFromMenu: Effect.Effect<
yield* electronDialog.showMessageBox({
type: "info",
title: "You're up to date!",
message: `TritonAI Code ${updateState.currentVersion} is currently the newest version available.`,
message: `TritonAI Harness ${updateState.currentVersion} is currently the newest version available.`,
buttons: ["OK"],
});
} else if (updateState.status === "error") {
Expand Down
10 changes: 5 additions & 5 deletions apps/marketing/src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ interface Props {
}

const {
title = "TritonAI Code",
description = "TritonAI Code — The open-source control plane for coding agents.",
title = "TritonAI Harness",
description = "TritonAI Harness — The open-source control plane for coding agents.",
} = Astro.props;
---

Expand All @@ -33,16 +33,16 @@ const {
<div class="page">
<nav class="nav">
<div class="nav-inner">
<a href="/" class="nav-brand" aria-label="TritonAI Code home">
<span class="nav-brand-name">TritonAI Code</span>
<a href="/" class="nav-brand" aria-label="TritonAI Harness home">
<span class="nav-brand-name">TritonAI Harness</span>
</a>
<div class="nav-right">
<a
class="nav-stars"
href={GITHUB_REPOSITORY_URL}
target="_blank"
rel="noopener noreferrer"
aria-label={`Star TritonAI Code on GitHub — ${MARKETING_STATS.githubStars} stars`}
aria-label={`Star TritonAI Harness on GitHub — ${MARKETING_STATS.githubStars} stars`}
>
<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2.3l2.9 5.88 6.49.94-4.7 4.58 1.11 6.47L12 17.12l-5.8 3.05 1.11-6.47-4.7-4.58 6.49-.94L12 2.3z"/>
Expand Down
4 changes: 2 additions & 2 deletions apps/marketing/src/pages/download.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import Layout from "../layouts/Layout.astro";
import { RELEASES_URL } from "../lib/releases";
---

<Layout title="Download — TritonAI Code" description="Download TritonAI Code for macOS, Windows, or Linux.">
<Layout title="Download — TritonAI Harness" description="Download TritonAI Harness for macOS, Windows, or Linux.">
<div class="download-page">
<h1 class="heading">Download TritonAI Code</h1>
<h1 class="heading">Download TritonAI Harness</h1>
<p class="subheading">
<span id="version-label">Loading latest release&hellip;</span>
<a id="changelog-link" class="changelog-link" href="#" target="_blank" rel="noopener noreferrer" style="display:none">View changelog<span aria-hidden="true"> &#8599;</span></a>
Expand Down
28 changes: 24 additions & 4 deletions apps/marketing/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ const mobileEndorsementRows = [

<div class="hero-preview">
<figure class="hero-screenshot-frame">
<span class="hero-screenshot-title" aria-hidden="true">TritonAI Harness</span>
<img
src="/updated-screenshot.webp"
alt="TritonAI Code app showing multiple coding agent threads in one workspace"
alt="TritonAI Harness app showing multiple coding agent threads in one workspace"
width="2508"
height="1682"
loading="eager"
Expand Down Expand Up @@ -172,7 +173,7 @@ const mobileEndorsementRows = [
<div class="section-head">
<h2>Bring your own sub</h2>
<p>
TritonAI Code doesn't resell tokens. Plug in Claude Code, Codex, OpenCode,
TritonAI Harness doesn't resell tokens. Plug in Claude Code, Codex, OpenCode,
or Cursor with the credentials you already have — we orchestrate
them, you keep your plan.
</p>
Expand Down Expand Up @@ -298,7 +299,7 @@ const mobileEndorsementRows = [
<div><span class="prompt">$</span> cd t3code &amp;&amp; vp install</div>
<div class="dim">✓ 1 284 packages installed in 4.2s</div>
<div><span class="prompt">$</span> vp run dev</div>
<div class="dim">▲ TritonAI Code dev server → <span class="accent">http://localhost:4001</span></div>
<div class="dim">▲ TritonAI Harness dev server → <span class="accent">http://localhost:4001</span></div>
<div class="cursor"><span class="prompt">$</span><span class="caret"></span></div>
</div>
</div>
Expand Down Expand Up @@ -351,7 +352,7 @@ const mobileEndorsementRows = [
Your agents deserve better than a terminal.
</h2>
<p class="cta-sub">
TritonAI Code is free, open source and fast. Install it, plug in the harness
TritonAI Harness is free, open source and fast. Install it, plug in the harness
you already pay for, and let your agents get to work.
</p>
<div class="cta-actions">
Expand Down Expand Up @@ -743,6 +744,25 @@ const mobileEndorsementRows = [
object-position: center top;
}

.hero-screenshot-title {
position: absolute;
z-index: 1;
top: 1.25%;
left: 6.4%;
width: 18.5%;
height: 3.6%;
display: flex;
align-items: center;
padding-left: 0.4%;
background: linear-gradient(90deg, #202024 0%, #202024 82%, rgba(32, 32, 36, 0) 100%);
color: rgba(244, 244, 245, 0.72);
font-size: clamp(8px, 0.92vw, 12px);
font-weight: 650;
line-height: 1;
pointer-events: none;
white-space: nowrap;
}

/* ── Harnesses ─────────────────────────────────────────── */
.sec-harness { background: #09090b; }

Expand Down
10 changes: 5 additions & 5 deletions apps/mobile/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# T3 Code Mobile
# TritonAI Harness Mobile

> [!WARNING]
> T3 Code Mobile is currently in development and is not distributed yet. If you want to try it out, you can build it from source.
> TritonAI Harness Mobile is currently in development and is not distributed yet. If you want to try it out, you can build it from source.

## Quickstart

Expand All @@ -10,9 +10,9 @@

This app has three variants:

- `development`: Expo dev client, installable side-by-side as `T3 Code Dev`
- `preview`: persistent internal preview build, installable side-by-side as `T3 Code Preview`
- `production`: store/release build as `T3 Code`
- `development`: Expo dev client, installable side-by-side as `TritonAI Harness Dev`
- `preview`: persistent internal preview build, installable side-by-side as `TritonAI Harness Preview`
- `production`: store/release build as `TritonAI Harness`

Run commands from `apps/mobile`.

Expand Down
13 changes: 7 additions & 6 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,21 @@ const VARIANT_CONFIG: Record<
}
> = {
development: {
appName: "T3 Code Dev",
appName: "TritonAI Harness Dev",
scheme: "t3code-dev",
iosIcon: "./assets/icon-composer-dev.icon",
iosBundleIdentifier: "com.t3tools.t3code.dev",
androidPackage: "com.t3tools.t3code.dev",
},
preview: {
appName: "T3 Code Preview",
appName: "TritonAI Harness Preview",
scheme: "t3code-preview",
iosIcon: "./assets/icon-composer-prod.icon",
iosBundleIdentifier: "com.t3tools.t3code.preview",
androidPackage: "com.t3tools.t3code.preview",
},
production: {
appName: "T3 Code",
appName: "TritonAI Harness",
scheme: "t3code",
iosIcon: "./assets/icon-composer-prod.icon",
iosBundleIdentifier: "com.t3tools.t3code",
Expand Down Expand Up @@ -82,7 +82,7 @@ const config: ExpoConfig = {
NSAllowsArbitraryLoads: true,
},
NSLocalNetworkUsageDescription:
"Allow T3 Code to connect to T3 Code servers on your local network or tailnet.",
"Allow TritonAI Harness to connect to TritonAI Harness servers on your local network or tailnet.",
ITSAppUsesNonExemptEncryption: false,
},
},
Expand All @@ -109,7 +109,8 @@ const config: ExpoConfig = {
[
"expo-camera",
{
cameraPermission: "Allow T3 Code to access your camera so you can scan pairing QR codes.",
cameraPermission:
"Allow TritonAI Harness to access your camera so you can scan pairing QR codes.",
barcodeScannerEnabled: true,
},
],
Expand Down Expand Up @@ -144,7 +145,7 @@ const config: ExpoConfig = {
{
name: "AgentActivity",
displayName: "Agent Activity",
description: "Shows the current state of active T3 Code agents.",
description: "Shows the current state of active TritonAI Harness agents.",
supportedFamilies: ["systemSmall", "systemMedium", "accessoryRectangular"],
},
],
Expand Down
Loading
Loading