File size: 8,529 Bytes
fd0a8b6
5b5da0e
 
 
 
 
 
 
 
 
 
cae0460
 
5017021
 
744d9e5
 
5017021
fd0a8b6
cae0460
fd0a8b6
 
 
5b5da0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bd25310
 
 
 
5b5da0e
 
 
 
 
 
 
 
94ef8c1
 
40f8c9b
 
 
ad23973
 
 
b60e122
 
 
585ab8a
b60e122
 
 
 
 
 
e9256c1
 
 
 
 
5b5da0e
 
 
6d51a81
5b5da0e
 
e34ce4a
5b5da0e
7d9aec4
8e5d9a0
7d9aec4
a3d2eef
a9cda9b
7d9aec4
7c91582
8e5d9a0
 
7d9aec4
 
 
 
e34ce4a
 
 
 
7d9aec4
 
 
9c5792a
 
 
 
 
 
 
 
 
 
604c3c1
 
 
 
 
 
 
37775da
604c3c1
 
5b5da0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import type { Cli, Group, MoveTarget, Session, Tree } from './types';

const HEADERS = { 'content-type': 'application/json' };
const json = (r: Response) => {
  if (!r.ok) throw new Error(`${r.status}`);
  return r.json();
};

export const getClis = (): Promise<Cli[]> => fetch('/api/clis').then(json);
export const getTree = (): Promise<Tree> => fetch('/api/tree').then(json);

// path: '.' = the workspaces root; anything else is a workspace-relative dir.
const normalizePath = (path?: string) => (path && path.trim() ? path : '.');
// Quickstart: create at the workspaces root, boot the CLI, and type the prompt
// as soon as it's up — all server-side, no waiting in the UI.
export const quickStart = (cli: string, prompt: string, name = '', path = '.'): Promise<Session> =>
  fetch('/api/sessions', { method: 'POST', headers: HEADERS, body: JSON.stringify({ cli, name: name || undefined, path: path || '.', prompt: prompt || undefined }) }).then(json);

export const createSession = (name: string, cli: string, groupId?: string, path?: string): Promise<Session> =>
  fetch('/api/sessions', { method: 'POST', headers: HEADERS, body: JSON.stringify({ name, cli, groupId, path: normalizePath(path) }) }).then(json);

export const listFolders = (p = ''): Promise<{ path: string; folders: string[] }> =>
  fetch(`/api/folders?path=${encodeURIComponent(p)}`).then(json);

export const stopSession = (id: string) =>
  fetch(`/api/sessions/${id}/stop`, { method: 'POST' }).then(json);

export const deleteSession = (id: string) =>
  fetch(`/api/sessions/${id}`, { method: 'DELETE' }).then(json);

export const renameSession = (id: string, name: string) =>
  fetch(`/api/sessions/${id}`, { method: 'PUT', headers: HEADERS, body: JSON.stringify({ name }) }).then(json);

export const createGroup = (name: string): Promise<Group> =>
  fetch('/api/groups', { method: 'POST', headers: HEADERS, body: JSON.stringify({ name }) }).then(json);

export const renameGroup = (id: string, name: string) =>
  fetch(`/api/groups/${id}`, { method: 'PUT', headers: HEADERS, body: JSON.stringify({ name }) }).then(json);

// Generic group patch: pane order (sessionIds) and/or tile layout (null = auto).
export const updateGroup = (id: string, patch: { sessionIds?: string[]; layout?: { cols: number; rows: number } | null }) =>
  fetch(`/api/groups/${id}`, { method: 'PUT', headers: HEADERS, body: JSON.stringify(patch) }).then(json);

export const deleteGroup = (id: string) =>
  fetch(`/api/groups/${id}`, { method: 'DELETE' }).then(json);

export const move = (ref: string, to: MoveTarget) =>
  fetch('/api/move', { method: 'POST', headers: HEADERS, body: JSON.stringify({ ref, to }) }).then(json);

export const getInfo = () => fetch('/api/info').then(json);

export const dismissWelcome = () => fetch('/api/welcome/seen', { method: 'POST' }).then(json);

export const setDemo = (active: boolean): Promise<{ ok: boolean; active: boolean }> =>
  fetch('/api/demo', { method: 'POST', headers: HEADERS, body: JSON.stringify({ active }) }).then(json);

export const relaunchSpace = (): Promise<{ ok: boolean; reason?: string }> =>
  fetch('/api/relaunch', { method: 'POST' }).then(json);

export interface AmConfig {
  artifacts: { enabled: boolean; space: string; visibility: 'public' | 'private' };
  jobs: { askAboveUsd: number };
  archive: { after: 'week' | 'month' | 'never' };
  defaultArtifactsSpace?: string;
}
export const getConfig = (): Promise<AmConfig> => fetch('/api/config').then(json);
export const saveConfig = (c: AmConfig) =>
  fetch('/api/config', { method: 'PUT', headers: HEADERS, body: JSON.stringify(c) }).then(json);

export interface SecretsData { detected: string[]; notes: Record<string, string>; }
export const getSecrets = (): Promise<SecretsData> => fetch('/api/secrets').then(json);
export const saveSecrets = (notes: Record<string, string>) =>
  fetch('/api/secrets', { method: 'PUT', headers: HEADERS, body: JSON.stringify({ notes }) }).then(json);

export interface QuotaWindow { usedPercent?: number; resetsAt?: number; windowMinutes?: number; }
export interface ProviderUsage {
  tokensToday?: number; costToday?: number; tokensWeek?: number; costWeek?: number; totalCost?: number;
  quota?: { fiveHour?: QuotaWindow; weekly?: QuotaWindow; opus?: QuotaWindow; updatedAt?: number; source?: 'live' | 'snapshot' } | null;
}
export interface Usage { providers: Record<string, ProviderUsage>; generatedAt: string; }
export const getUsage = (provider?: string): Promise<Usage> => fetch(provider ? `/api/usage?provider=${provider}` : '/api/usage').then(json);

// ---- overview (meta) ----
export interface TurnEntry { answer: string; answerMd: string; ts: number; }
export interface MetaDigest {
  lastPromptText: string; lastPromptRaw?: string; lastPromptTs: number;
  lastAssistantText: string; lastAssistantMd: string; lastAssistantTs: number;
  sinceTurns: number; sinceToolCalls: number; sinceTools: Record<string, number>; sinceFiles: string[];
  sinceTokens: number;
  running?: boolean;        // task in flight (codex task_started/task_complete)
  turnsLog?: TurnEntry[];   // newest-first history of completed exchanges
}
export interface MetaSession extends Session { digest: MetaDigest | null }
export const getMeta = (): Promise<{ sessions: MetaSession[]; generatedAt: string }> =>
  fetch('/api/meta').then(json);
// Targeted digest for one session (progressive tile fill); digest is null when
// this CLI only resolves through the bulk pass.
export const getMetaOne = (id: string): Promise<{ id: string; digest: MetaDigest | null }> =>
  fetch(`/api/meta/${id}`).then(json);
export const sendInput = (id: string, text: string): Promise<{ ok: boolean; started?: boolean }> =>
  fetch(`/api/sessions/${id}/input`, { method: 'POST', headers: HEADERS, body: JSON.stringify({ text }) }).then(json);

// ---- push notifications ----
export const getPushKey = (): Promise<{ publicKey: string; devices: number }> =>
  fetch('/api/push/key').then(json);
export const subscribePush = (subscription: PushSubscription) =>
  fetch('/api/push/subscribe', { method: 'POST', headers: HEADERS, body: JSON.stringify({ subscription }) }).then(json);
export const unsubscribePush = (endpoint: string) =>
  fetch('/api/push/unsubscribe', { method: 'POST', headers: HEADERS, body: JSON.stringify({ endpoint }) }).then(json);
export const sendTestNotification = (): Promise<{ ok: boolean; sent: number; devices: number }> =>
  fetch('/api/notify', { method: 'POST', headers: HEADERS, body: JSON.stringify({ title: 'Agent Manager', body: 'Test notification — agents can reach this device.' }) }).then(json);

// ---- trace analytics ----
export interface TraceStats {
  turns: number; prompts: number; toolCalls: number; tools: Record<string, number>;
  web: number; tokensIn: number; tokensOut: number; cacheRead: number;
  firstTs: number; lastTs: number; files: number;
}
export interface SessionTraces extends TraceStats { id: string; name: string; cli: string; path: string | null; }
export interface Traces { sessions: SessionTraces[]; totals: TraceStats; generatedAt: string; }
export const getTraces = (): Promise<Traces> => fetch('/api/traces').then(json);

// ---- files ----
export interface FileEntry { name: string; dir: boolean; size: number; }
export interface FileListing { path: string; root: string; entries: FileEntry[]; }

export const listFiles = (id: string, p = ''): Promise<FileListing> =>
  fetch(`/api/files/${id}?path=${encodeURIComponent(p)}`).then(json);

export const uploadFile = (id: string, p: string, file: File) =>
  fetch(`/api/files/${id}/upload?path=${encodeURIComponent(p)}&name=${encodeURIComponent(file.name)}`, {
    method: 'POST', headers: { 'content-type': 'application/octet-stream' }, body: file,
  }).then(json);

export const downloadUrl = (id: string, p: string) =>
  `/api/files/${id}/download?path=${encodeURIComponent(p)}`;

// ---- skills ----
export interface SkillFile { name: string; size: number; }
export const listSkills = (): Promise<SkillFile[]> => fetch('/api/skills').then(json);
export const getSkill = (name: string): Promise<{ name: string; content: string }> =>
  fetch(`/api/skills/${encodeURIComponent(name)}`).then(json);
export const saveSkill = (name: string, content: string) =>
  fetch(`/api/skills/${encodeURIComponent(name)}`, { method: 'PUT', headers: { 'content-type': 'text/plain' }, body: content }).then(json);
export const deleteSkill = (name: string) =>
  fetch(`/api/skills/${encodeURIComponent(name)}`, { method: 'DELETE' }).then(json);