@gaia-ai/addon-gaia-ui 0.6.1
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 +5 -0
- package/dist/src/Component/agent-list/index.d.ts +6 -0
- package/dist/src/Component/agent-list/index.js +14 -0
- package/dist/src/Component/comment-item/index.d.ts +21 -0
- package/dist/src/Component/comment-item/index.js +39 -0
- package/dist/src/Component/create-form/index.d.ts +3 -0
- package/dist/src/Component/create-form/index.js +26 -0
- package/dist/src/Component/dashboard-list/index.d.ts +8 -0
- package/dist/src/Component/dashboard-list/index.js +7 -0
- package/dist/src/Component/run-table/index.d.ts +10 -0
- package/dist/src/Component/run-table/index.js +20 -0
- package/dist/src/Component/section-list/index.d.ts +9 -0
- package/dist/src/Component/section-list/index.js +27 -0
- package/dist/src/Component/tab-bar/index.d.ts +9 -0
- package/dist/src/Component/tab-bar/index.js +22 -0
- package/dist/src/Component/ticket-detail/index.d.ts +30 -0
- package/dist/src/Component/ticket-detail/index.js +129 -0
- package/dist/src/Component/ticket-table/index.d.ts +5 -0
- package/dist/src/Component/ticket-table/index.js +14 -0
- package/dist/src/Component/top-tabs/index.d.ts +8 -0
- package/dist/src/Component/top-tabs/index.js +13 -0
- package/dist/src/Deriver/liveness.d.ts +12 -0
- package/dist/src/Deriver/liveness.js +34 -0
- package/dist/src/Deriver/run-status.d.ts +12 -0
- package/dist/src/Deriver/run-status.js +36 -0
- package/dist/src/Kernel/tui-kernel.d.ts +2 -0
- package/dist/src/Kernel/tui-kernel.js +983 -0
- package/dist/src/Screen/dashboard.d.ts +1 -0
- package/dist/src/Screen/dashboard.js +1 -0
- package/dist/src/Screen/project.d.ts +1 -0
- package/dist/src/Screen/project.js +1 -0
- package/dist/src/Screen/ticket.d.ts +1 -0
- package/dist/src/Screen/ticket.js +1 -0
- package/dist/src/Service/agent.d.ts +17 -0
- package/dist/src/Service/agent.js +28 -0
- package/dist/src/Service/create-form-data.d.ts +10 -0
- package/dist/src/Service/create-form-data.js +52 -0
- package/dist/src/Service/create-form.d.ts +52 -0
- package/dist/src/Service/create-form.js +98 -0
- package/dist/src/Service/project-data.d.ts +48 -0
- package/dist/src/Service/project-data.js +92 -0
- package/dist/src/Service/projects.d.ts +21 -0
- package/dist/src/Service/projects.js +80 -0
- package/dist/src/Service/run-data.d.ts +35 -0
- package/dist/src/Service/run-data.js +62 -0
- package/dist/src/Service/ticket-data.d.ts +6 -0
- package/dist/src/Service/ticket-data.js +7 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +10 -0
- package/dist/src/launcher.d.ts +58 -0
- package/dist/src/launcher.js +51 -0
- package/dist/src/lib/ansi.d.ts +19 -0
- package/dist/src/lib/ansi.js +45 -0
- package/dist/src/lib/format.d.ts +3 -0
- package/dist/src/lib/format.js +10 -0
- package/dist/src/lib/scroll.d.ts +1 -0
- package/dist/src/lib/scroll.js +16 -0
- package/dist/src/lib/ticket-query.d.ts +21 -0
- package/dist/src/lib/ticket-query.js +72 -0
- package/dist/src/plugin.d.ts +13 -0
- package/dist/src/plugin.js +17 -0
- package/dist/src/preset.d.ts +2 -0
- package/dist/src/preset.js +7 -0
- package/dist/src/types.d.ts +98 -0
- package/dist/src/types.js +5 -0
- package/package.json +28 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type GaiaUiOptions } from './plugin.js';
|
|
2
|
+
import type { TuiAgentService } from './types.js';
|
|
3
|
+
/** A dropsh program: only the `parseAsync` we drive is modelled. */
|
|
4
|
+
export interface DropshProgram {
|
|
5
|
+
parseAsync(argv: string[], opts: {
|
|
6
|
+
from: 'user';
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
}
|
|
9
|
+
/** The subset of dropsh's `buildProgram` the launcher uses (injected). */
|
|
10
|
+
export type DropshBuildProgram = (opts: {
|
|
11
|
+
plugins: unknown[];
|
|
12
|
+
}) => DropshProgram;
|
|
13
|
+
/**
|
|
14
|
+
* The resolved home control-plane connection. `authPlugins` are the dropsh auth
|
|
15
|
+
* plugin descriptors/instances (a `session` oauth2 client-credentials entry);
|
|
16
|
+
* `configPath` points dropsh's own in-process context factory at a home dropsh
|
|
17
|
+
* config (base_url + the same auth) so it can build its client WITHOUT a project
|
|
18
|
+
* `conductor.config.js` — the conductor entry synthesizes it.
|
|
19
|
+
*/
|
|
20
|
+
export interface HomeConnection {
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
jsonapiPrefix?: string | undefined;
|
|
23
|
+
authPlugins: unknown[];
|
|
24
|
+
configPath?: string | undefined;
|
|
25
|
+
/** The dropsh auth profile the cockpit reads as. The home cockpit reads
|
|
26
|
+
* `gaia_conductor`/`gaia_run` + tickets, which are owner-gated to `session`
|
|
27
|
+
* (a `pm` read returns them empty) — so this pins the profile regardless of
|
|
28
|
+
* whatever profile a prior `gaia dropsh` login left active. */
|
|
29
|
+
authProfile?: string | undefined;
|
|
30
|
+
}
|
|
31
|
+
export interface GaiaUiLauncherOptions {
|
|
32
|
+
/** Resolved home connection (base_url + auth + dropsh config path). */
|
|
33
|
+
connection: HomeConnection;
|
|
34
|
+
/** Repo/home root the launched agents run in. */
|
|
35
|
+
cwd: string;
|
|
36
|
+
/** Default project (opaque group), from the conductor registry. */
|
|
37
|
+
project?: string | undefined;
|
|
38
|
+
/** Herdr-backed agent host, already adapted prompt→command (injected). */
|
|
39
|
+
agents?: TuiAgentService | undefined;
|
|
40
|
+
/** Preselected new-ticket assignee (this machine's user id). */
|
|
41
|
+
currentUser?: string | undefined;
|
|
42
|
+
/** This machine's conductor `machine_id` — preselected in the new-ticket form. */
|
|
43
|
+
ownConductorMachineId?: string | undefined;
|
|
44
|
+
/** Config override for the ticket-create agent's base prompt. */
|
|
45
|
+
createPrompt?: string | undefined;
|
|
46
|
+
/** Injected dropsh `buildProgram` (the entry passes dropsh's real one). */
|
|
47
|
+
buildProgram: DropshBuildProgram;
|
|
48
|
+
/** Injected renderer factory; defaults to this package's own plugin. The
|
|
49
|
+
* `$GAIA_UI_PLUGIN` dev override resolves a different module in the entry. */
|
|
50
|
+
renderer?: ((opts: GaiaUiOptions) => unknown) | undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build + run the dropsh `--format tui` program with the TUI renderer and the
|
|
54
|
+
* injected agent host. A trivial `search gaia_project` seeds the renderer's
|
|
55
|
+
* `run()`; the renderer discards the seed document and boots its own dashboard
|
|
56
|
+
* from the conductor registry (`~/.gaia/conductors.json`).
|
|
57
|
+
*/
|
|
58
|
+
export declare function runGaiaUi(opts: GaiaUiLauncherOptions): Promise<void>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import gaiaUiPlugin from './plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build + run the dropsh `--format tui` program with the TUI renderer and the
|
|
4
|
+
* injected agent host. A trivial `search gaia_project` seeds the renderer's
|
|
5
|
+
* `run()`; the renderer discards the seed document and boots its own dashboard
|
|
6
|
+
* from the conductor registry (`~/.gaia/conductors.json`).
|
|
7
|
+
*/
|
|
8
|
+
export async function runGaiaUi(opts) {
|
|
9
|
+
// Point dropsh's own in-process context factory at the home dropsh config so
|
|
10
|
+
// it resolves base_url + auth without a project config. An explicit
|
|
11
|
+
// $DROPSH_CONFIG (or a prior set) still wins.
|
|
12
|
+
if (opts.connection.configPath && !process.env.DROPSH_CONFIG) {
|
|
13
|
+
process.env.DROPSH_CONFIG = opts.connection.configPath;
|
|
14
|
+
}
|
|
15
|
+
// Pin the read profile (session) so dropsh does not fall through to a
|
|
16
|
+
// previously-active profile (`pm`) whose provider the home config omits — and
|
|
17
|
+
// whose owner-gated reads would come back empty. An explicit env still wins.
|
|
18
|
+
if (opts.connection.authProfile && !process.env.DROPSH_AUTH_PROFILE) {
|
|
19
|
+
process.env.DROPSH_AUTH_PROFILE = opts.connection.authProfile;
|
|
20
|
+
}
|
|
21
|
+
const makeRenderer = opts.renderer ?? gaiaUiPlugin;
|
|
22
|
+
const renderer = makeRenderer({
|
|
23
|
+
cwd: opts.cwd,
|
|
24
|
+
...(opts.project !== undefined ? { project: opts.project } : {}),
|
|
25
|
+
...(opts.agents ? { agents: opts.agents } : {}),
|
|
26
|
+
...(opts.currentUser !== undefined
|
|
27
|
+
? { currentUser: opts.currentUser }
|
|
28
|
+
: {}),
|
|
29
|
+
...(opts.ownConductorMachineId !== undefined
|
|
30
|
+
? { ownConductorMachineId: opts.ownConductorMachineId }
|
|
31
|
+
: {}),
|
|
32
|
+
...(opts.createPrompt !== undefined
|
|
33
|
+
? { createPrompt: opts.createPrompt }
|
|
34
|
+
: {}),
|
|
35
|
+
});
|
|
36
|
+
const program = opts.buildProgram({
|
|
37
|
+
plugins: [...opts.connection.authPlugins, renderer],
|
|
38
|
+
});
|
|
39
|
+
// Any JSON:API document with `--format tui` drives the renderer; it discards
|
|
40
|
+
// the seed and loads the dashboard itself.
|
|
41
|
+
await program.parseAsync([
|
|
42
|
+
'--format',
|
|
43
|
+
'tui',
|
|
44
|
+
'search',
|
|
45
|
+
'gaia_project',
|
|
46
|
+
'--bundle',
|
|
47
|
+
'gaia_project',
|
|
48
|
+
'--limit',
|
|
49
|
+
'1',
|
|
50
|
+
], { from: 'user' });
|
|
51
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare const ANSI: {
|
|
2
|
+
reset: string;
|
|
3
|
+
bold: string;
|
|
4
|
+
dim: string;
|
|
5
|
+
cyan: string;
|
|
6
|
+
brightCyan: string;
|
|
7
|
+
inverse: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function sanitizeDisplay(value: unknown): string;
|
|
10
|
+
export declare function fitLine(value: unknown, width: number): string;
|
|
11
|
+
export interface StyleVisibleLinesOptions {
|
|
12
|
+
lines: string[];
|
|
13
|
+
boldRows?: Set<number> | undefined;
|
|
14
|
+
selRow?: number;
|
|
15
|
+
offset?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
contentWidth: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function styleVisibleLines({ lines, boldRows, selRow, offset, height, contentWidth, }: StyleVisibleLinesOptions): string[];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const ANSI = {
|
|
2
|
+
reset: '\x1b[0m',
|
|
3
|
+
bold: '\x1b[1m',
|
|
4
|
+
dim: '\x1b[2m',
|
|
5
|
+
cyan: '\x1b[36m',
|
|
6
|
+
brightCyan: '\x1b[96m',
|
|
7
|
+
inverse: '\x1b[7m',
|
|
8
|
+
};
|
|
9
|
+
export function sanitizeDisplay(value) {
|
|
10
|
+
// Defense-in-depth so fitLine() structurally guarantees ONE physical row per field:
|
|
11
|
+
// map every C0 whitespace control (TAB, LF, VT, FF, CR) to a space — keeping
|
|
12
|
+
// words separated — then strip the remaining C0/C1 controls. A single field can
|
|
13
|
+
// therefore never carry an embedded newline/tab into a "single row".
|
|
14
|
+
return (String(value)
|
|
15
|
+
.replace(/[\t\n\v\f\r]/g, ' ')
|
|
16
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping the C0/C1 control range is the whole point of this sanitizer.
|
|
17
|
+
.replace(/[\x00-\x1f\x7f-\x9f]/g, ''));
|
|
18
|
+
}
|
|
19
|
+
export function fitLine(value, width) {
|
|
20
|
+
const text = sanitizeDisplay(value);
|
|
21
|
+
return text.length > width
|
|
22
|
+
? `${text.slice(0, Math.max(0, width - 1))}…`
|
|
23
|
+
: text.padEnd(width);
|
|
24
|
+
}
|
|
25
|
+
// Fit + style the visible window: selected row inverse, heading rows bold —
|
|
26
|
+
// both applied AFTER fitLine() so the ANSI wraps the sanitized text, never the
|
|
27
|
+
// reverse (fitting an ANSI-wrapped string strips the ESC and leaks CSI text).
|
|
28
|
+
//
|
|
29
|
+
// INVARIANT (AC-4): every returned entry is exactly ONE physical row. fitLine()
|
|
30
|
+
// runs sanitizeDisplay(), which maps LF/TAB to space and strips the rest of the
|
|
31
|
+
// C0/C1 range, so no entry can carry an embedded newline — draw()'s fixed-height
|
|
32
|
+
// window therefore emits Math.min(height, lines.length) physical rows, never
|
|
33
|
+
// more. ansi.test.ts pins the primitive; Component/ticket-detail/index.test.ts
|
|
34
|
+
// pins it against a real multi-line detail document.
|
|
35
|
+
export function styleVisibleLines({ lines, boldRows, selRow = -1, offset = 0, height = lines.length, contentWidth, }) {
|
|
36
|
+
return lines.slice(offset, offset + height).map((line, index) => {
|
|
37
|
+
const abs = offset + index;
|
|
38
|
+
const content = ` ${fitLine(line, contentWidth)}`;
|
|
39
|
+
if (abs === selRow)
|
|
40
|
+
return `${ANSI.inverse}${content}${ANSI.reset}`;
|
|
41
|
+
if (boldRows?.has(abs))
|
|
42
|
+
return `${ANSI.bold}${content}${ANSI.reset}`;
|
|
43
|
+
return content;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function pad(value, width) {
|
|
2
|
+
const s = String(value ?? '');
|
|
3
|
+
return s.length > width
|
|
4
|
+
? `${s.slice(0, Math.max(0, width - 1))}…`
|
|
5
|
+
: s.padEnd(width);
|
|
6
|
+
}
|
|
7
|
+
/** Read a value as a string, or `undefined` when it is not one. */
|
|
8
|
+
export function asString(value) {
|
|
9
|
+
return typeof value === 'string' ? value : undefined;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function scrollTo(selRow: number, offset: number, height: number): number;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Clamp a viewport of `height` rows starting at `offset` so the selected row
|
|
2
|
+
// stays visible: scroll up when it is above the window, down when it is at or
|
|
3
|
+
// below the bottom edge, leave it untouched when already inside.
|
|
4
|
+
//
|
|
5
|
+
// `selRow` is the ABSOLUTE physical row of the selection. List screens prepend a
|
|
6
|
+
// header row (the ticket table's `IDENTIFIER …` line), so the caller must pass
|
|
7
|
+
// screenView().selRow — the real row of the highlight — NOT the 0-based
|
|
8
|
+
// selection index. Feeding the 0-based index here is the AC-2 off-by-one the
|
|
9
|
+
// round-5 review caught: the highlight fell one row past the window bottom.
|
|
10
|
+
export function scrollTo(selRow, offset, height) {
|
|
11
|
+
if (selRow < offset)
|
|
12
|
+
return selRow;
|
|
13
|
+
if (selRow >= offset + height)
|
|
14
|
+
return selRow - height + 1;
|
|
15
|
+
return offset;
|
|
16
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
|
|
2
|
+
/** The active search + filter criteria for the project ticket list. */
|
|
3
|
+
export interface TicketCriteria {
|
|
4
|
+
query: string;
|
|
5
|
+
state: string | null;
|
|
6
|
+
type: string | null;
|
|
7
|
+
}
|
|
8
|
+
export declare const STATE_FILTER_VALUES: string[];
|
|
9
|
+
export declare const TYPE_FILTER_VALUES: string[];
|
|
10
|
+
export declare const TYPE_LABELS: Record<string, string>;
|
|
11
|
+
/** Next value in a cyclic list; `null` (== "all") wraps at both ends. */
|
|
12
|
+
export declare function cycleValue(values: string[], current: string | null): string | null;
|
|
13
|
+
export declare function hasActiveCriteria(criteria: TicketCriteria): boolean;
|
|
14
|
+
/** Human-readable active-criteria summary, or '' when none. */
|
|
15
|
+
export declare function describeCriteria(criteria: TicketCriteria): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build the JSON:API query for a project's tickets under the given criteria.
|
|
18
|
+
* project + state + type are AND filters at the top level; the free-text search
|
|
19
|
+
* is an OR group matching identifier OR title (substring).
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildTicketQueryParams(projectId: string, criteria: TicketCriteria): DrupalJsonApiParams;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
|
|
2
|
+
// Ticket states from web/modules/custom/gaia_core/gaia_core.workflows.yml
|
|
3
|
+
// (feature/bug/chore workflows) plus the import/backlog states seen on tickets.
|
|
4
|
+
// Hardcoded so the cycle is deterministic and independent of the loaded page.
|
|
5
|
+
export const STATE_FILTER_VALUES = [
|
|
6
|
+
'triage',
|
|
7
|
+
'backlog',
|
|
8
|
+
'todo',
|
|
9
|
+
'spec',
|
|
10
|
+
'diagnose',
|
|
11
|
+
'coding',
|
|
12
|
+
'review',
|
|
13
|
+
'done',
|
|
14
|
+
'cancelled',
|
|
15
|
+
];
|
|
16
|
+
export const TYPE_FILTER_VALUES = [
|
|
17
|
+
'gaia_feature',
|
|
18
|
+
'gaia_bug',
|
|
19
|
+
'gaia_chore',
|
|
20
|
+
];
|
|
21
|
+
export const TYPE_LABELS = {
|
|
22
|
+
gaia_feature: 'feature',
|
|
23
|
+
gaia_bug: 'bug',
|
|
24
|
+
gaia_chore: 'chore',
|
|
25
|
+
};
|
|
26
|
+
/** Next value in a cyclic list; `null` (== "all") wraps at both ends. */
|
|
27
|
+
export function cycleValue(values, current) {
|
|
28
|
+
if (current == null)
|
|
29
|
+
return values[0] ?? null;
|
|
30
|
+
const i = values.indexOf(current);
|
|
31
|
+
if (i < 0 || i === values.length - 1)
|
|
32
|
+
return null;
|
|
33
|
+
return values[i + 1] ?? null;
|
|
34
|
+
}
|
|
35
|
+
export function hasActiveCriteria(criteria) {
|
|
36
|
+
return (Boolean(criteria.query) || criteria.state != null || criteria.type != null);
|
|
37
|
+
}
|
|
38
|
+
/** Human-readable active-criteria summary, or '' when none. */
|
|
39
|
+
export function describeCriteria(criteria) {
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (criteria.query)
|
|
42
|
+
parts.push(`search:"${criteria.query}"`);
|
|
43
|
+
if (criteria.state)
|
|
44
|
+
parts.push(`status:${criteria.state}`);
|
|
45
|
+
if (criteria.type)
|
|
46
|
+
parts.push(`type:${TYPE_LABELS[criteria.type] ?? criteria.type}`);
|
|
47
|
+
return parts.join(' · ');
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build the JSON:API query for a project's tickets under the given criteria.
|
|
51
|
+
* project + state + type are AND filters at the top level; the free-text search
|
|
52
|
+
* is an OR group matching identifier OR title (substring).
|
|
53
|
+
*/
|
|
54
|
+
export function buildTicketQueryParams(projectId, criteria) {
|
|
55
|
+
const params = new DrupalJsonApiParams();
|
|
56
|
+
params.addFilter('project_id.id', projectId);
|
|
57
|
+
if (criteria.query) {
|
|
58
|
+
params.addGroup('search', 'OR');
|
|
59
|
+
params.addFilter('identifier', criteria.query, 'CONTAINS', 'search');
|
|
60
|
+
params.addFilter('title', criteria.query, 'CONTAINS', 'search');
|
|
61
|
+
}
|
|
62
|
+
if (criteria.state)
|
|
63
|
+
params.addFilter('state', criteria.state);
|
|
64
|
+
if (criteria.type)
|
|
65
|
+
params.addFilter('workflow', criteria.type);
|
|
66
|
+
// Sideload the parent ticket + assigned conductor so the row can render the
|
|
67
|
+
// parent identifier / conductor label from `included` (GAIA-193, AC-5).
|
|
68
|
+
params.addInclude(['parent_id', 'conductor_id']);
|
|
69
|
+
params.addSort('changed', 'DESC');
|
|
70
|
+
params.addPageLimit(50);
|
|
71
|
+
return params;
|
|
72
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RendererServices, TuiOptions } from './types.js';
|
|
2
|
+
/** Options the launcher passes into the factory (a superset of the injected
|
|
3
|
+
* intent-level agent service so `gaia ui` can drive launch/list/focus/…). */
|
|
4
|
+
export type GaiaUiOptions = TuiOptions;
|
|
5
|
+
export default function gaiaUiPlugin(options?: GaiaUiOptions): {
|
|
6
|
+
id: string;
|
|
7
|
+
requiredModules: string[];
|
|
8
|
+
renderers: {
|
|
9
|
+
id: string;
|
|
10
|
+
interactive: boolean;
|
|
11
|
+
run: (document: unknown, context: unknown, services: RendererServices) => Promise<void>;
|
|
12
|
+
}[];
|
|
13
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { runTicketTui } from './Kernel/tui-kernel.js';
|
|
2
|
+
// The dropsh `tui` renderer plugin: a single interactive renderer that hands off
|
|
3
|
+
// to the TUI kernel. The conductor's plugin resolver auto-picks the default
|
|
4
|
+
// export, so the factory is the module default.
|
|
5
|
+
export default function gaiaUiPlugin(options = {}) {
|
|
6
|
+
return {
|
|
7
|
+
id: 'gaia-ticket-renderer',
|
|
8
|
+
requiredModules: [],
|
|
9
|
+
renderers: [
|
|
10
|
+
{
|
|
11
|
+
id: 'tui',
|
|
12
|
+
interactive: true,
|
|
13
|
+
run: (document, context, services) => runTicketTui(document, context, services, options),
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import gaiaUiPlugin from './index.js';
|
|
2
|
+
export const connectionPlugins = (acc, opts) => [
|
|
3
|
+
...acc,
|
|
4
|
+
// The tui renderer plugin is a DropSHPlugin structurally (id + renderers);
|
|
5
|
+
// its concrete renderer types are wider than dropsh's exported interface.
|
|
6
|
+
gaiaUiPlugin(opts),
|
|
7
|
+
];
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/** A dropsh `toResource(...)` wrapper — see tests/test-support.ts. */
|
|
2
|
+
export interface Resource {
|
|
3
|
+
id: string;
|
|
4
|
+
type: string;
|
|
5
|
+
attr(key: string): unknown;
|
|
6
|
+
rel(key: string): string | null;
|
|
7
|
+
rels(key: string): string[];
|
|
8
|
+
}
|
|
9
|
+
/** A dropsh collection query builder (fluent). */
|
|
10
|
+
export interface Collection {
|
|
11
|
+
where(path: string, op: string, value: unknown): Collection;
|
|
12
|
+
sort(field: string, direction: string): Collection;
|
|
13
|
+
page(size: number): Collection;
|
|
14
|
+
first(): Promise<Resource | null>;
|
|
15
|
+
list(): Promise<Resource[]>;
|
|
16
|
+
}
|
|
17
|
+
/** The dropsh JSON:API client exposed as `services.client`. */
|
|
18
|
+
export interface DropshClient {
|
|
19
|
+
collection(type: string): Collection;
|
|
20
|
+
get(path: string, params?: unknown): Promise<unknown>;
|
|
21
|
+
}
|
|
22
|
+
/** A JSON:API relationship linkage. */
|
|
23
|
+
export interface JsonApiRelationship {
|
|
24
|
+
data?: {
|
|
25
|
+
id: string;
|
|
26
|
+
} | Array<{
|
|
27
|
+
id: string;
|
|
28
|
+
}> | null;
|
|
29
|
+
}
|
|
30
|
+
/** A JSON:API resource object (the ticket entity + its sideloaded includes). */
|
|
31
|
+
export interface JsonApiResource {
|
|
32
|
+
type?: string;
|
|
33
|
+
id?: string;
|
|
34
|
+
attributes?: Record<string, unknown>;
|
|
35
|
+
relationships?: Record<string, JsonApiRelationship | undefined>;
|
|
36
|
+
}
|
|
37
|
+
/** A ticket-detail document: primary resource + sideloaded includes. */
|
|
38
|
+
export interface TicketDetailDocument {
|
|
39
|
+
data?: JsonApiResource;
|
|
40
|
+
included?: JsonApiResource[];
|
|
41
|
+
}
|
|
42
|
+
/** A hosted agent as reported by the injected agent service (herdr-backed in
|
|
43
|
+
* production). `id` is the host-owned stable handle, `group` the project it was
|
|
44
|
+
* launched under, `label` a human tag for the list. */
|
|
45
|
+
export interface HostedAgent {
|
|
46
|
+
id: string;
|
|
47
|
+
group: string;
|
|
48
|
+
label: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Intent-level agent hosting the TUI drives (GAIA-190). The TUI says launch /
|
|
52
|
+
* list / focus / hide / kill by an opaque `group` (the project) + an `id` — it
|
|
53
|
+
* never names panes, tabs, splits or zoom. The host (herdr) owns all layout.
|
|
54
|
+
* We don't know what the next herdr does; this intent contract survives a
|
|
55
|
+
* different pane model unchanged.
|
|
56
|
+
*/
|
|
57
|
+
export interface TuiAgentService {
|
|
58
|
+
/** Start an agent for a group; the host places + shows it. */
|
|
59
|
+
launch(opts: {
|
|
60
|
+
group: string;
|
|
61
|
+
cwd: string;
|
|
62
|
+
prompt: string;
|
|
63
|
+
env: Record<string, string | undefined>;
|
|
64
|
+
isNew?: boolean | undefined;
|
|
65
|
+
label?: string | undefined;
|
|
66
|
+
}): Promise<HostedAgent>;
|
|
67
|
+
/** The live agents of a group (source of truth for the list + restart). */
|
|
68
|
+
list(group: string): Promise<HostedAgent[]>;
|
|
69
|
+
/** Bring an agent to the foreground. */
|
|
70
|
+
focus(id: string): Promise<void>;
|
|
71
|
+
/** Stop showing a group's agents, back to the caller. Durable — never kills. */
|
|
72
|
+
hide(group: string): Promise<void>;
|
|
73
|
+
/** Terminate an agent for good. */
|
|
74
|
+
kill(id: string): Promise<void>;
|
|
75
|
+
}
|
|
76
|
+
/** The dropsh renderer `services` bag the TUI consumes. */
|
|
77
|
+
export interface RendererServices {
|
|
78
|
+
client: DropshClient;
|
|
79
|
+
baseUrl?: string;
|
|
80
|
+
project?: string;
|
|
81
|
+
/** Intent-level agent host for `gaia ui` ticket-agents (herdr-backed). */
|
|
82
|
+
agents?: TuiAgentService;
|
|
83
|
+
/** This machine's user id — preselected as the new-ticket assignee. */
|
|
84
|
+
currentUser?: string;
|
|
85
|
+
/** This machine's conductor `machine_id` — preselected in the new-ticket form. */
|
|
86
|
+
ownConductorMachineId?: string;
|
|
87
|
+
/** Config override for the ticket-create agent's base prompt. */
|
|
88
|
+
createPrompt?: string;
|
|
89
|
+
}
|
|
90
|
+
/** Options passed by the factory into the kernel. */
|
|
91
|
+
export interface TuiOptions {
|
|
92
|
+
project?: string;
|
|
93
|
+
cwd?: string;
|
|
94
|
+
agents?: TuiAgentService;
|
|
95
|
+
currentUser?: string;
|
|
96
|
+
ownConductorMachineId?: string;
|
|
97
|
+
createPrompt?: string;
|
|
98
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Shared shapes for the dropsh renderer inputs + the JSON:API documents the TUI
|
|
2
|
+
// reads. These model the *contract* the plugin consumes at runtime (the dropsh
|
|
3
|
+
// client, its `toResource(...)` wrappers, and the ticket-detail document) so the
|
|
4
|
+
// modules stay strictly typed without leaking `any`.
|
|
5
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gaia-ai/addon-gaia-ui",
|
|
3
|
+
"version": "0.6.1",
|
|
4
|
+
"description": "GAIA TUI: interactive terminal UI (project dashboard, ticket list, ticket detail) as a dropsh `tui` renderer plugin.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/src/index.js",
|
|
9
|
+
"./preset": "./dist/src/preset.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/src"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://git.key-tec.de/keytec/gaia.git",
|
|
20
|
+
"directory": "gaia-cli/addons/gaia-ui"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"drupal-jsonapi-params": "^3.0.1"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"@gaia-ai/core": "^0.6.1"
|
|
27
|
+
}
|
|
28
|
+
}
|