@opencode-cockpit/trust 0.0.0-stage → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +241 -2
- package/dist/cli/preview.js +251 -0
- package/dist/core/adapt/seen.js +24 -0
- package/dist/core/adapt/v1.js +109 -0
- package/dist/core/adapt/v2.js +109 -0
- package/dist/core/config.js +93 -0
- package/dist/core/danger.js +366 -0
- package/dist/core/engine.js +245 -0
- package/dist/core/family.js +512 -0
- package/dist/core/history.js +169 -0
- package/dist/core/index.js +16 -0
- package/dist/core/keys.js +149 -0
- package/dist/core/ledger.js +202 -0
- package/dist/core/paths.js +38 -0
- package/dist/core/policy.js +128 -0
- package/dist/core/rules.js +152 -0
- package/dist/core/sample.js +311 -0
- package/dist/core/shell.js +253 -0
- package/dist/core/signature.js +38 -0
- package/dist/core/view/actions.js +321 -0
- package/dist/core/view/activity.js +557 -0
- package/dist/core/view/explorer.js +970 -0
- package/dist/core/view/model.js +193 -0
- package/dist/core/view/parts.js +290 -0
- package/dist/core/view/rows.js +165 -0
- package/dist/core/view/sidebar.js +170 -0
- package/dist/tui/index.js +920 -0
- package/dist/tui/journal.js +80 -0
- package/dist/tui/render.js +85 -0
- package/dist/tui/source.js +112 -0
- package/dist/tui/view/dialog.js +43 -0
- package/dist/tui/view/rows.js +69 -0
- package/package.json +61 -4
- package/tui.js +6 -0
- package/types/cli/preview.d.ts +22 -0
- package/types/core/adapt/seen.d.ts +46 -0
- package/types/core/adapt/v1.d.ts +18 -0
- package/types/core/adapt/v2.d.ts +20 -0
- package/types/core/config.d.ts +57 -0
- package/types/core/danger.d.ts +50 -0
- package/types/core/engine.d.ts +117 -0
- package/types/core/family.d.ts +97 -0
- package/types/core/history.d.ts +81 -0
- package/types/core/index.d.ts +16 -0
- package/types/core/keys.d.ts +68 -0
- package/types/core/ledger.d.ts +158 -0
- package/types/core/paths.d.ts +24 -0
- package/types/core/policy.d.ts +52 -0
- package/types/core/rules.d.ts +55 -0
- package/types/core/sample.d.ts +22 -0
- package/types/core/shell.d.ts +40 -0
- package/types/core/signature.d.ts +18 -0
- package/types/core/view/actions.d.ts +76 -0
- package/types/core/view/activity.d.ts +101 -0
- package/types/core/view/explorer.d.ts +154 -0
- package/types/core/view/model.d.ts +106 -0
- package/types/core/view/parts.d.ts +76 -0
- package/types/core/view/rows.d.ts +60 -0
- package/types/core/view/sidebar.d.ts +62 -0
- package/types/tui/index.d.ts +15 -0
- package/types/tui/journal.d.ts +19 -0
- package/types/tui/render.d.ts +14 -0
- package/types/tui/source.d.ts +35 -0
- package/types/tui/view/dialog.d.ts +24 -0
- package/types/tui/view/rows.d.ts +19 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rows of styled runs — every Trust surface, pure so a test can measure it.
|
|
3
|
+
*
|
|
4
|
+
* Tones name a meaning, never a colour; `tui/render.ts` is the only place that knows what a tone
|
|
5
|
+
* looks like, so the preview CLI and OpenCode draw the same rows (docs/building/terminal-ui.md).
|
|
6
|
+
* The helpers are Subagents' (`core/view/rows.ts`), copied rather than imported: bays never import
|
|
7
|
+
* each other.
|
|
8
|
+
*/
|
|
9
|
+
export type Tone = "text" | "muted" | "accent" | "info" | "tool" | "success" | "error" | "warning" | "border"
|
|
10
|
+
/** Text cut out of a solid fill (a focused button): the colour of what the dialog is drawn on. */
|
|
11
|
+
| "ink";
|
|
12
|
+
/**
|
|
13
|
+
* What sits behind a run. Two cover a whole row — the row under the cursor and the card's raised
|
|
14
|
+
* panel — and the rest a few cells: an agent's chip, a button, a focused button, and the tinted
|
|
15
|
+
* badges that say success, warning and error. A tint is the tone's colour faded into the dialog's
|
|
16
|
+
* own, so it follows the theme (`TINTS`; `tui/render.ts` mixes it).
|
|
17
|
+
*/
|
|
18
|
+
export type Fill = "none" | "selected" | "panel" | "chip" | "button" | "buttonOn" | "ok" | "warn" | "err";
|
|
19
|
+
/**
|
|
20
|
+
* The tinted fills: which tone each is made of, and how much of it is mixed into the dialog's
|
|
21
|
+
* background. Little enough that the tone's own text on it stays legible; enough to be seen.
|
|
22
|
+
*/
|
|
23
|
+
export declare const TINTS: Readonly<Record<"chip" | "ok" | "warn" | "err", {
|
|
24
|
+
tone: Tone;
|
|
25
|
+
amount: number;
|
|
26
|
+
}>>;
|
|
27
|
+
export interface Run {
|
|
28
|
+
text: string;
|
|
29
|
+
tone?: Tone;
|
|
30
|
+
fill?: Fill;
|
|
31
|
+
bold?: boolean;
|
|
32
|
+
faint?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export type Row = Run[];
|
|
35
|
+
export declare const rowText: (row: Row) => string;
|
|
36
|
+
/** Columns a string takes. Wide characters (CJK, most emoji) take two. */
|
|
37
|
+
export declare function widthOf(text: string, limit?: number): number;
|
|
38
|
+
/** `text` in at most `width` columns, ending in `…` when cut. */
|
|
39
|
+
export declare function cut(text: string, width: number): string;
|
|
40
|
+
/**
|
|
41
|
+
* A row exactly `width` columns wide: runs cut where they overflow, padded in the last run's fill
|
|
42
|
+
* where they fall short. Every row every surface draws goes through here; the grid test holds it.
|
|
43
|
+
*/
|
|
44
|
+
export declare function fit(row: Row, width: number): Row;
|
|
45
|
+
/** The fill a row is drawn on: the last row-wide fill among its runs. A chip at its end is not one. */
|
|
46
|
+
export declare function rowFillOf(row: Row): Fill | undefined;
|
|
47
|
+
/** Left and right parts on one row, the right part flush against the edge; the left gives way. */
|
|
48
|
+
export declare function spread(left: Row, right: Row, width: number): Row;
|
|
49
|
+
/**
|
|
50
|
+
* A row on one surface — the row under the cursor, the card's panel. A run with a fill of its own
|
|
51
|
+
* (a chip, a badge, a button) keeps it: the surface is what is behind them.
|
|
52
|
+
*/
|
|
53
|
+
export declare const filled: (row: Row, fill: Fill) => Row;
|
|
54
|
+
/**
|
|
55
|
+
* The row under the cursor: `▌` in its first cell — the margin every row keeps for it — and the
|
|
56
|
+
* selected fill to both edges (docs/building/design-system.md: `▌` is the cursor and nothing else).
|
|
57
|
+
*/
|
|
58
|
+
export declare function cursorRow(row: Row, width: number): Row;
|
|
59
|
+
/** How long ago, in the fewest characters that still read: `now`, `5m`, `2h`, `3d`. */
|
|
60
|
+
export declare function ago(ms: number): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sidebar block: what Trust answered for you in this window, and what it is counting now.
|
|
3
|
+
*
|
|
4
|
+
* Trust 4 auto
|
|
5
|
+
* ● git status 7×
|
|
6
|
+
* ● edit src/app.ts 3×
|
|
7
|
+
* ○ docker compose up 2/3
|
|
8
|
+
*
|
|
9
|
+
* A filled dot is an answer Trust gave, with how many times it has answered that in all; a hollow
|
|
10
|
+
* one is a request on screen now and how far it is from being trusted. Answers are visible every
|
|
11
|
+
* time — that is the bargain that makes answering for you acceptable — but the block says nothing
|
|
12
|
+
* when there is nothing to say: no answers yet, nothing counting, not paused. A failure always speaks.
|
|
13
|
+
*
|
|
14
|
+
* Asked for (`shown`: the setting, or the palette's toggle), the heading says where the project
|
|
15
|
+
* stands rather than only this window — `Trust 2 auto · 5 trusted · 1 counting` — since a window
|
|
16
|
+
* just opened has answered nothing yet while the ledger holds plenty. It is never silent then: a
|
|
17
|
+
* project with nothing learned says so, or showing the block looks like a command that did nothing.
|
|
18
|
+
*
|
|
19
|
+
* An answer given through a family you widened says so — `● ls -R docs · any ls 1×` — because
|
|
20
|
+
* that rule was never approved by itself, and the answer should not look as if it had been.
|
|
21
|
+
* Commands are shown the way the ledger shows them (`family.showSubject`): `echo "---"`, never a
|
|
22
|
+
* bare `---` a font can merge into a line.
|
|
23
|
+
*
|
|
24
|
+
* Plain geometric marks, not ⚡: the emoji draws two columns wide in most terminals and one in some,
|
|
25
|
+
* which breaks a grid that has to be exact; Subagents' sidebar uses the same dots.
|
|
26
|
+
*/
|
|
27
|
+
import type { Answered, Pending } from "../engine.ts";
|
|
28
|
+
import { type State, type Thresholds } from "../ledger.ts";
|
|
29
|
+
import { type Row } from "./rows.ts";
|
|
30
|
+
export interface SidebarInput {
|
|
31
|
+
width: number;
|
|
32
|
+
/** Answers in this window, newest first. */
|
|
33
|
+
recent: readonly Answered[];
|
|
34
|
+
/** How many answers in this window. */
|
|
35
|
+
count: number;
|
|
36
|
+
pending: readonly Pending[];
|
|
37
|
+
state: State;
|
|
38
|
+
/** Something went wrong and you should know: drawn whatever else is going on. */
|
|
39
|
+
trouble?: string;
|
|
40
|
+
/** Answers listed; the count in the heading covers the rest. */
|
|
41
|
+
limit: number;
|
|
42
|
+
/**
|
|
43
|
+
* You asked for the block: its heading tallies the project (`project`), and it draws even with
|
|
44
|
+
* nothing to say this window.
|
|
45
|
+
*/
|
|
46
|
+
shown?: boolean;
|
|
47
|
+
/** The project's commands, from the whole ledger (`tally`). */
|
|
48
|
+
project?: Tally;
|
|
49
|
+
}
|
|
50
|
+
/** Commands in the project's ledger: trusted by some agent, or on their way there. */
|
|
51
|
+
export interface Tally {
|
|
52
|
+
trusted: number;
|
|
53
|
+
counting: number;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* One count per command, however many agents it was approved for — as the ledger lists them: trusted
|
|
57
|
+
* when any agent trusts it, counting when none does yet but one has approvals that still count.
|
|
58
|
+
*/
|
|
59
|
+
export declare function tally(state: State, settings: Thresholds, now: number): Tally;
|
|
60
|
+
/** `edit src/app.ts`, but a command is just the command: bash is the common case. */
|
|
61
|
+
export declare function labelOf(permission: string, label: string): Row;
|
|
62
|
+
export declare function sidebarRows(input: SidebarInput): Row[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import { type Host } from "@opencode-cockpit/client/host";
|
|
3
|
+
import { type TrustConfig } from "../core/config.ts";
|
|
4
|
+
export type TrustTuiOptions = TrustConfig;
|
|
5
|
+
/** Trust's interface half as a factory, so the `opencode-cockpit` bundle can include it. */
|
|
6
|
+
export declare function createTrustTui({ source }?: {
|
|
7
|
+
source?: string;
|
|
8
|
+
}): (api: Host, rawOptions?: unknown) => Promise<void>;
|
|
9
|
+
/** One entry for both OpenCodes: v1 calls `tui`, v2 calls `setup` (docs/opencode/v2.md). */
|
|
10
|
+
declare const _default: {
|
|
11
|
+
id: string;
|
|
12
|
+
tui: (api: import("@opencode-ai/plugin/tui").TuiPluginApi, options?: unknown) => Promise<void>;
|
|
13
|
+
setup: (ctx: import("@opencode-cockpit/client/host").V2Context) => Promise<() => void>;
|
|
14
|
+
};
|
|
15
|
+
export default _default;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ledger file, read and appended without ever blocking the interface thread.
|
|
3
|
+
*
|
|
4
|
+
* Every read and every append goes through one chain, so this window's writes and reads never
|
|
5
|
+
* overlap each other (the lesson of Review's trace: unserialised appends dropped lines). Other
|
|
6
|
+
* windows append to the same file between our reads; `read` takes whatever whole lines arrived since
|
|
7
|
+
* the last one and leaves a line still being written for next time.
|
|
8
|
+
*/
|
|
9
|
+
import { type Event } from "../core/ledger.ts";
|
|
10
|
+
import type { TrustPaths } from "../core/paths.ts";
|
|
11
|
+
export interface Journal {
|
|
12
|
+
/** Events added since the last read, by anyone, in file order. `reset`: the file was replaced. */
|
|
13
|
+
read: () => Promise<{
|
|
14
|
+
events: Event[];
|
|
15
|
+
reset: boolean;
|
|
16
|
+
}>;
|
|
17
|
+
append: (events: readonly Event[]) => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createJournal(paths: TrustPaths): Journal;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The only place that knows what a tone looks like: every colour from the user's OpenCode theme,
|
|
3
|
+
* through the host (so OpenCode 2's tokens arrive under the same names — client/host `themeFromV2`).
|
|
4
|
+
*/
|
|
5
|
+
import type { Theme } from "@opencode-cockpit/client/host";
|
|
6
|
+
import type { RGBA } from "@opentui/core";
|
|
7
|
+
import { type Fill, type Tone } from "../core/view/rows.ts";
|
|
8
|
+
export declare function toneColour(theme: Theme, tone: Tone | undefined): RGBA;
|
|
9
|
+
/**
|
|
10
|
+
* What sits behind a run. The row-wide surfaces and the buttons are the theme's raised element; a
|
|
11
|
+
* focused button is the accent; a chip or a badge is its tone's tint, and — on a theme whose dialog
|
|
12
|
+
* has no colour to tint — no fill at all: the word keeps its tone and still says what it is.
|
|
13
|
+
*/
|
|
14
|
+
export declare function fillColour(theme: Theme, fill: Fill | undefined): RGBA | undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust's reach into a specific OpenCode: its events, its reply, its config and its pending list.
|
|
3
|
+
* The only file past `Host` — through `api.v1` / `api.v2`, as Subagents' source does — and every call
|
|
4
|
+
* was measured or read from the version's own client (docs/opencode/permissions.md):
|
|
5
|
+
*
|
|
6
|
+
* | | OpenCode 1 | OpenCode 2 |
|
|
7
|
+
* | --- | --- | --- |
|
|
8
|
+
* | events | `api.event.on(type)` | `ctx.data.listen` |
|
|
9
|
+
* | answer | `client.permission.reply({ requestID, reply })` | `client.permission.reply({ sessionID, requestID, decision })` |
|
|
10
|
+
* | config | `client.config.get()` — merged | `client.config.get({ location })` — documents, lowest first |
|
|
11
|
+
* | pending | `client.permission.list()` | not read (see `pending` below) |
|
|
12
|
+
*
|
|
13
|
+
* What it hears becomes `Seen` through the pure adapters in `core/adapt/`.
|
|
14
|
+
*/
|
|
15
|
+
import type { Host } from "@opencode-cockpit/client/host";
|
|
16
|
+
import type { Log } from "@opencode-cockpit/client/log";
|
|
17
|
+
import { type Seen } from "../core/adapt/seen.ts";
|
|
18
|
+
import type { Request } from "../core/keys.ts";
|
|
19
|
+
export interface Source {
|
|
20
|
+
/** Says "once" to a request: the only answer Trust ever gives. */
|
|
21
|
+
approve: (request: Request) => Promise<void>;
|
|
22
|
+
/** OpenCode's config, as its `config.get` returns it, for `rulesFrom`. */
|
|
23
|
+
config: () => Promise<unknown>;
|
|
24
|
+
/** Requests pending now, by the host's own account; undefined when it cannot say. */
|
|
25
|
+
pending: () => Promise<Seen[] | undefined>;
|
|
26
|
+
/** The call's command line from the host's own store, when its events have not said it yet. */
|
|
27
|
+
call: (request: Request) => {
|
|
28
|
+
line?: string;
|
|
29
|
+
workdir?: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/** The agent a session runs as, from the host's own store. */
|
|
32
|
+
agent: (sessionID: string, messageID?: string) => string | undefined;
|
|
33
|
+
dispose: () => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function createSource(api: Host, log: Log, emit: (seen: Seen[]) => void): Source;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { Host, Layer } from "@opencode-cockpit/client/host";
|
|
3
|
+
import type { JSX } from "solid-js";
|
|
4
|
+
import type { Row } from "../../core/view/rows.ts";
|
|
5
|
+
export interface DialogProps {
|
|
6
|
+
api: Host;
|
|
7
|
+
rows: () => readonly Row[];
|
|
8
|
+
/**
|
|
9
|
+
* The dialog's keys. Registered from inside the dialog, as Shell's console does: while the host's
|
|
10
|
+
* dialog is open it takes the keys, so a global layer would never hear them, and a layer owned by
|
|
11
|
+
* the component goes when the dialog does — on either OpenCode.
|
|
12
|
+
*/
|
|
13
|
+
keys: () => Layer;
|
|
14
|
+
/** The wheel moves the cursor, three rows a notch, and the list follows it as `j`/`k` do. */
|
|
15
|
+
onScroll: (by: number) => void;
|
|
16
|
+
/** A click, in the rows' own cells: column and row from their top-left corner. */
|
|
17
|
+
onClick: (x: number, y: number) => void;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Trust's dialog in the host's: rows from `core/view/` — the activity or the ledger — and the keys and
|
|
21
|
+
* clicks that act on them. Clicks are read on release: on press, anything a click opens is up in time
|
|
22
|
+
* to catch the release and take it for its own (Review's overlay learned this).
|
|
23
|
+
*/
|
|
24
|
+
export declare function Dialog(props: DialogProps): JSX.Element;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** @jsxImportSource @opentui/solid */
|
|
2
|
+
import type { Host } from "@opencode-cockpit/client/host";
|
|
3
|
+
import type { BoxRenderable } from "@opentui/core";
|
|
4
|
+
import { type JSX } from "solid-js";
|
|
5
|
+
import type { Row } from "../../core/view/rows.ts";
|
|
6
|
+
export interface RowsProps {
|
|
7
|
+
api: Host;
|
|
8
|
+
/** The rows `core/view/` produced; a signal, so `<For>` redraws them. */
|
|
9
|
+
rows: () => readonly Row[];
|
|
10
|
+
onReady?: (box: BoxRenderable) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Rows of runs, one `<text>` per row — the drawing both the sidebar block and the ledger dialog use.
|
|
14
|
+
*
|
|
15
|
+
* Shell's and Subagents' pattern, proven live on both OpenCodes: a signal read by `<For>` redraws,
|
|
16
|
+
* where anything decided once in a slot's tree never would (docs/opencode/gotchas.md, "Slots").
|
|
17
|
+
* The shape never changes; only the list of rows does.
|
|
18
|
+
*/
|
|
19
|
+
export declare function Rows(props: RowsProps): JSX.Element;
|