@otto-code/client 0.5.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/README.md +12 -0
- package/dist/compat/normalize-provider-models.d.ts +11 -0
- package/dist/compat/normalize-provider-models.js +42 -0
- package/dist/daemon-client-relay-e2ee-transport.d.ts +8 -0
- package/dist/daemon-client-relay-e2ee-transport.js +161 -0
- package/dist/daemon-client-runtime-metrics.d.ts +39 -0
- package/dist/daemon-client-runtime-metrics.js +173 -0
- package/dist/daemon-client-transport-types.d.ts +36 -0
- package/dist/daemon-client-transport-types.js +2 -0
- package/dist/daemon-client-transport-utils.d.ts +9 -0
- package/dist/daemon-client-transport-utils.js +121 -0
- package/dist/daemon-client-transport.d.ts +5 -0
- package/dist/daemon-client-transport.js +4 -0
- package/dist/daemon-client-websocket-transport.d.ts +8 -0
- package/dist/daemon-client-websocket-transport.js +120 -0
- package/dist/daemon-client.d.ts +1179 -0
- package/dist/daemon-client.js +4192 -0
- package/dist/index.d.ts +281 -0
- package/dist/index.js +176 -0
- package/dist/terminal-stream-router.d.ts +28 -0
- package/dist/terminal-stream-router.js +108 -0
- package/package.json +47 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import type { AgentSnapshotPayload, CreateAgentRequestMessage, FetchWorkspacesRequestMessage, FetchWorkspacesResponseMessage, GetProvidersSnapshotResponseMessage, ListAvailableProvidersResponse, ListProviderFeaturesRequestMessage, ListProviderFeaturesResponseMessage, ListProviderModelsResponseMessage, ListProviderModesResponseMessage, MutableDaemonConfig, MutableDaemonConfigPatch, ProviderDiagnosticResponseMessage, ProjectPlacementPayload, RefreshProvidersSnapshotResponseMessage, SendAgentMessageRequest, SessionOutboundMessage, WorkspaceDescriptorPayload } from "@otto-code/protocol/messages";
|
|
2
|
+
import { DaemonClient } from "./daemon-client.js";
|
|
3
|
+
import type { FetchAgentTimelineCursor, FetchAgentTimelineDirection, FetchAgentTimelinePayload, FetchAgentTimelineProjection } from "./daemon-client.js";
|
|
4
|
+
export { DaemonClient };
|
|
5
|
+
export type { DaemonClientConfig, DaemonEvent, BrowserAutomationExecuteRequestMessage, BrowserAutomationExecuteResponseMessage, HostingAuthStatusPayload, HostingSearchPayload, WebSocketFactory, WebSocketLike, } from "./daemon-client.js";
|
|
6
|
+
export type ConnectionState = {
|
|
7
|
+
status: "idle";
|
|
8
|
+
} | {
|
|
9
|
+
status: "connecting";
|
|
10
|
+
attempt: number;
|
|
11
|
+
} | {
|
|
12
|
+
status: "connected";
|
|
13
|
+
} | {
|
|
14
|
+
status: "disconnected";
|
|
15
|
+
reason?: string;
|
|
16
|
+
} | {
|
|
17
|
+
status: "disposed";
|
|
18
|
+
};
|
|
19
|
+
export interface OttoLogger {
|
|
20
|
+
debug(obj: object, msg?: string): void;
|
|
21
|
+
info(obj: object, msg?: string): void;
|
|
22
|
+
warn(obj: object, msg?: string): void;
|
|
23
|
+
error(obj: object, msg?: string): void;
|
|
24
|
+
}
|
|
25
|
+
export interface OttoClientConfig {
|
|
26
|
+
url: string;
|
|
27
|
+
clientId?: string;
|
|
28
|
+
appVersion?: string;
|
|
29
|
+
runtimeGeneration?: number | null;
|
|
30
|
+
password?: string;
|
|
31
|
+
authHeader?: string;
|
|
32
|
+
suppressSendErrors?: boolean;
|
|
33
|
+
logger?: OttoLogger;
|
|
34
|
+
connectTimeoutMs?: number;
|
|
35
|
+
e2ee?: {
|
|
36
|
+
enabled?: boolean;
|
|
37
|
+
daemonPublicKeyB64?: string;
|
|
38
|
+
};
|
|
39
|
+
reconnect?: {
|
|
40
|
+
enabled?: boolean;
|
|
41
|
+
baseDelayMs?: number;
|
|
42
|
+
maxDelayMs?: number;
|
|
43
|
+
};
|
|
44
|
+
runtimeMetricsIntervalMs?: number;
|
|
45
|
+
runtimeMetricsWindowMs?: number;
|
|
46
|
+
}
|
|
47
|
+
export type OttoWorkspace = WorkspaceDescriptorPayload;
|
|
48
|
+
export type OttoAgent = AgentSnapshotPayload;
|
|
49
|
+
export type OttoWorkspaceListOptions = Omit<FetchWorkspacesRequestMessage, "type" | "requestId"> & {
|
|
50
|
+
requestId?: string;
|
|
51
|
+
};
|
|
52
|
+
export interface OttoWorkspaceListResult {
|
|
53
|
+
requestId: string;
|
|
54
|
+
subscriptionId?: string | null;
|
|
55
|
+
entries: OttoWorkspace[];
|
|
56
|
+
pageInfo: FetchWorkspacesResponseMessage["payload"]["pageInfo"];
|
|
57
|
+
}
|
|
58
|
+
export interface OttoWorkspaceOpenOptions {
|
|
59
|
+
cwd: string;
|
|
60
|
+
requestId?: string;
|
|
61
|
+
}
|
|
62
|
+
export interface OttoWorkspaceOpenResult {
|
|
63
|
+
requestId: string;
|
|
64
|
+
workspace: OttoWorkspaceHandle | null;
|
|
65
|
+
error: string | null;
|
|
66
|
+
}
|
|
67
|
+
export interface OttoWorkspaceArchiveResult {
|
|
68
|
+
requestId: string;
|
|
69
|
+
workspaceId: string;
|
|
70
|
+
archivedAt: string | null;
|
|
71
|
+
error: string | null;
|
|
72
|
+
}
|
|
73
|
+
export type OttoWorkspaceUpdate = Extract<SessionOutboundMessage, {
|
|
74
|
+
type: "workspace_update";
|
|
75
|
+
}>["payload"];
|
|
76
|
+
export type OttoWorkspaceUpdateHandler = (update: OttoWorkspaceUpdate) => void;
|
|
77
|
+
/**
|
|
78
|
+
* A handle is a stable typed reference to a daemon resource. Its identity is the
|
|
79
|
+
* daemon id, and `latest()` only returns the most recent snapshot this handle has
|
|
80
|
+
* seen through construction, `refetch()`, or this handle's local subscription.
|
|
81
|
+
*/
|
|
82
|
+
export interface OttoWorkspaceHandle {
|
|
83
|
+
readonly id: string;
|
|
84
|
+
latest(): OttoWorkspace | null;
|
|
85
|
+
/**
|
|
86
|
+
* Fetches a fresh workspace snapshot through the existing workspace list RPC,
|
|
87
|
+
* exact-matches this handle id from the result, and updates `latest()`.
|
|
88
|
+
*/
|
|
89
|
+
refetch(options?: {
|
|
90
|
+
requestId?: string;
|
|
91
|
+
}): Promise<OttoWorkspace | null>;
|
|
92
|
+
archive(requestId?: string): Promise<OttoWorkspaceArchiveResult>;
|
|
93
|
+
/**
|
|
94
|
+
* Subscribes to already-emitted daemon workspace_update events for this id.
|
|
95
|
+
* This returns a local unsubscribe function; it does not own app cache state or
|
|
96
|
+
* send a daemon unsubscribe RPC. Call `workspaces.list({ subscribe: {} })` when
|
|
97
|
+
* the daemon should start streaming workspace directory updates.
|
|
98
|
+
*/
|
|
99
|
+
subscribe(handler: (update: OttoWorkspaceUpdate) => void): () => void;
|
|
100
|
+
}
|
|
101
|
+
export interface OttoWorkspaceActions {
|
|
102
|
+
list(options?: OttoWorkspaceListOptions): Promise<OttoWorkspaceListResult>;
|
|
103
|
+
ref(workspace: string | OttoWorkspace): OttoWorkspaceHandle;
|
|
104
|
+
open(input: string | OttoWorkspaceOpenOptions, requestId?: string): Promise<OttoWorkspaceOpenResult>;
|
|
105
|
+
create(input: string | OttoWorkspaceOpenOptions, requestId?: string): Promise<OttoWorkspaceOpenResult>;
|
|
106
|
+
archive(workspace: string | OttoWorkspaceHandle, requestId?: string): Promise<OttoWorkspaceArchiveResult>;
|
|
107
|
+
/**
|
|
108
|
+
* Local event subscription over the low-level driver's workspace_update stream.
|
|
109
|
+
* The returned function only removes this SDK listener.
|
|
110
|
+
*/
|
|
111
|
+
subscribe(handler: OttoWorkspaceUpdateHandler): () => void;
|
|
112
|
+
}
|
|
113
|
+
type OttoAgentSessionConfig = CreateAgentRequestMessage["config"];
|
|
114
|
+
type OttoAgentProvider = OttoAgentSessionConfig["provider"];
|
|
115
|
+
type OttoAgentConfigOverrides = Partial<Omit<OttoAgentSessionConfig, "provider" | "cwd">>;
|
|
116
|
+
export interface OttoAgentCreateOptions extends OttoAgentConfigOverrides {
|
|
117
|
+
config?: OttoAgentSessionConfig;
|
|
118
|
+
provider?: CreateAgentRequestMessage["config"]["provider"];
|
|
119
|
+
cwd?: string;
|
|
120
|
+
workspaceId?: string;
|
|
121
|
+
initialPrompt?: string;
|
|
122
|
+
clientMessageId?: string;
|
|
123
|
+
outputSchema?: Record<string, unknown>;
|
|
124
|
+
images?: CreateAgentRequestMessage["images"];
|
|
125
|
+
attachments?: CreateAgentRequestMessage["attachments"];
|
|
126
|
+
git?: CreateAgentRequestMessage["git"];
|
|
127
|
+
worktreeName?: string;
|
|
128
|
+
requestId?: string;
|
|
129
|
+
labels?: Record<string, string>;
|
|
130
|
+
}
|
|
131
|
+
export interface OttoAgentRefetchResult {
|
|
132
|
+
agent: OttoAgent;
|
|
133
|
+
project: ProjectPlacementPayload | null;
|
|
134
|
+
}
|
|
135
|
+
export interface OttoAgentTimelineRefetchOptions {
|
|
136
|
+
direction?: FetchAgentTimelineDirection;
|
|
137
|
+
cursor?: FetchAgentTimelineCursor;
|
|
138
|
+
limit?: number;
|
|
139
|
+
projection?: FetchAgentTimelineProjection;
|
|
140
|
+
requestId?: string;
|
|
141
|
+
}
|
|
142
|
+
export interface OttoAgentSendOptions {
|
|
143
|
+
messageId?: string;
|
|
144
|
+
images?: Array<{
|
|
145
|
+
data: string;
|
|
146
|
+
mimeType: string;
|
|
147
|
+
}>;
|
|
148
|
+
attachments?: SendAgentMessageRequest["attachments"];
|
|
149
|
+
}
|
|
150
|
+
export type OttoAgentUpdate = Extract<SessionOutboundMessage, {
|
|
151
|
+
type: "agent_update";
|
|
152
|
+
}>["payload"];
|
|
153
|
+
export type OttoAgentStream = Extract<SessionOutboundMessage, {
|
|
154
|
+
type: "agent_stream";
|
|
155
|
+
}>["payload"];
|
|
156
|
+
export type OttoAgentUpdateHandler = (update: OttoAgentUpdate) => void;
|
|
157
|
+
export interface OttoAgentTimelineHandle {
|
|
158
|
+
/**
|
|
159
|
+
* Fetches a fresh timeline page through the existing daemon RPC. If the daemon
|
|
160
|
+
* includes an agent snapshot in the response, the parent handle's `latest()`
|
|
161
|
+
* is updated to that snapshot.
|
|
162
|
+
*/
|
|
163
|
+
refetch(options?: OttoAgentTimelineRefetchOptions): Promise<FetchAgentTimelinePayload>;
|
|
164
|
+
/**
|
|
165
|
+
* Local listener for agent_stream events matching this handle id. It does not
|
|
166
|
+
* retain timeline entries or own application cache state.
|
|
167
|
+
*/
|
|
168
|
+
subscribe(handler: (event: OttoAgentStream) => void): () => void;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Agent handles follow the same identity/snapshot rule as workspace handles:
|
|
172
|
+
* `id` is stable, while `latest()` is only the newest snapshot observed by this
|
|
173
|
+
* handle through construction, `refetch()`, timeline refetch, archive, or local
|
|
174
|
+
* agent_update subscription.
|
|
175
|
+
*/
|
|
176
|
+
export interface OttoAgentHandle {
|
|
177
|
+
readonly id: string;
|
|
178
|
+
readonly timeline: OttoAgentTimelineHandle;
|
|
179
|
+
latest(): OttoAgent | null;
|
|
180
|
+
refetch(requestId?: string): Promise<OttoAgentRefetchResult | null>;
|
|
181
|
+
send(text: string, options?: OttoAgentSendOptions): Promise<void>;
|
|
182
|
+
archive(): Promise<{
|
|
183
|
+
archivedAt: string;
|
|
184
|
+
}>;
|
|
185
|
+
detach(): Promise<void>;
|
|
186
|
+
subscribe(handler: (update: OttoAgentUpdate) => void): () => void;
|
|
187
|
+
}
|
|
188
|
+
export interface OttoAgentActions {
|
|
189
|
+
ref(agent: string | OttoAgent): OttoAgentHandle;
|
|
190
|
+
create(options: OttoAgentCreateOptions): Promise<OttoAgentHandle>;
|
|
191
|
+
/**
|
|
192
|
+
* Local event subscription over the low-level driver's agent_update stream.
|
|
193
|
+
* The returned function only removes this SDK listener.
|
|
194
|
+
*/
|
|
195
|
+
subscribe(handler: OttoAgentUpdateHandler): () => void;
|
|
196
|
+
}
|
|
197
|
+
export interface OttoProviderConfig extends OttoProviderConfigInput {
|
|
198
|
+
provider: OttoAgentProvider;
|
|
199
|
+
}
|
|
200
|
+
export type OttoProviderFeatureValues = Record<string, unknown>;
|
|
201
|
+
export interface OttoProviderConfigInput {
|
|
202
|
+
model?: string;
|
|
203
|
+
modeId?: string;
|
|
204
|
+
thinkingOptionId?: string;
|
|
205
|
+
featureValues?: OttoProviderFeatureValues;
|
|
206
|
+
}
|
|
207
|
+
export type OttoProviderModelsResult = ListProviderModelsResponseMessage["payload"];
|
|
208
|
+
export type OttoProviderModesResult = ListProviderModesResponseMessage["payload"];
|
|
209
|
+
export type OttoProviderFeaturesInput = ListProviderFeaturesRequestMessage["draftConfig"];
|
|
210
|
+
export type OttoProviderFeaturesResult = ListProviderFeaturesResponseMessage["payload"];
|
|
211
|
+
export type OttoProviderAvailabilityResult = ListAvailableProvidersResponse["payload"];
|
|
212
|
+
export type OttoProviderSnapshotResult = GetProvidersSnapshotResponseMessage["payload"];
|
|
213
|
+
export type OttoProviderSnapshotUpdate = Extract<SessionOutboundMessage, {
|
|
214
|
+
type: "providers_snapshot_update";
|
|
215
|
+
}>["payload"];
|
|
216
|
+
export type OttoProviderRefreshResult = RefreshProvidersSnapshotResponseMessage["payload"];
|
|
217
|
+
export type OttoProviderDiagnosticResult = ProviderDiagnosticResponseMessage["payload"];
|
|
218
|
+
export interface OttoProviderListOptions {
|
|
219
|
+
cwd?: string;
|
|
220
|
+
requestId?: string;
|
|
221
|
+
}
|
|
222
|
+
export interface OttoProviderRefreshOptions {
|
|
223
|
+
cwd?: string;
|
|
224
|
+
providers?: OttoAgentProvider[];
|
|
225
|
+
requestId?: string;
|
|
226
|
+
}
|
|
227
|
+
export interface OttoProviderActions {
|
|
228
|
+
codex(input?: OttoProviderConfigInput): OttoProviderConfig;
|
|
229
|
+
claude(input?: OttoProviderConfigInput): OttoProviderConfig;
|
|
230
|
+
opencode(input?: OttoProviderConfigInput): OttoProviderConfig;
|
|
231
|
+
copilot(input?: OttoProviderConfigInput): OttoProviderConfig;
|
|
232
|
+
config(provider: OttoAgentProvider, input?: OttoProviderConfigInput): OttoProviderConfig;
|
|
233
|
+
listModels(provider: OttoAgentProvider, options?: OttoProviderListOptions): Promise<OttoProviderModelsResult>;
|
|
234
|
+
listModes(provider: OttoAgentProvider, options?: OttoProviderListOptions): Promise<OttoProviderModesResult>;
|
|
235
|
+
listFeatures(draftConfig: OttoProviderFeaturesInput, options?: {
|
|
236
|
+
requestId?: string;
|
|
237
|
+
}): Promise<OttoProviderFeaturesResult>;
|
|
238
|
+
listAvailable(options?: {
|
|
239
|
+
requestId?: string;
|
|
240
|
+
}): Promise<OttoProviderAvailabilityResult>;
|
|
241
|
+
snapshot(options?: OttoProviderListOptions): Promise<OttoProviderSnapshotResult>;
|
|
242
|
+
refresh(options?: OttoProviderRefreshOptions): Promise<OttoProviderRefreshResult>;
|
|
243
|
+
diagnostic(provider: OttoAgentProvider, options?: {
|
|
244
|
+
requestId?: string;
|
|
245
|
+
}): Promise<OttoProviderDiagnosticResult>;
|
|
246
|
+
subscribe(handler: (update: OttoProviderSnapshotUpdate) => void): () => void;
|
|
247
|
+
}
|
|
248
|
+
export interface OttoConfigActions {
|
|
249
|
+
/**
|
|
250
|
+
* Reads daemon config through the existing config RPC. Provider profiles,
|
|
251
|
+
* custom provider entries, keys/env, custom binaries, and provider enablement
|
|
252
|
+
* are currently config-file-shaped daemon state, so the SDK exposes this raw
|
|
253
|
+
* typed surface instead of pretending there are higher-level provider-settings
|
|
254
|
+
* RPCs.
|
|
255
|
+
*/
|
|
256
|
+
get(requestId?: string): Promise<{
|
|
257
|
+
requestId: string;
|
|
258
|
+
config: MutableDaemonConfig;
|
|
259
|
+
}>;
|
|
260
|
+
/**
|
|
261
|
+
* Patches daemon config through the existing config RPC. The daemon validates
|
|
262
|
+
* and persists supported fields; unsupported provider/settings workflows remain
|
|
263
|
+
* daemon gaps until first-class RPCs exist.
|
|
264
|
+
*/
|
|
265
|
+
patch(config: MutableDaemonConfigPatch, requestId?: string): Promise<{
|
|
266
|
+
requestId: string;
|
|
267
|
+
config: MutableDaemonConfig;
|
|
268
|
+
}>;
|
|
269
|
+
}
|
|
270
|
+
export interface OttoClient {
|
|
271
|
+
readonly workspaces: OttoWorkspaceActions;
|
|
272
|
+
readonly agents: OttoAgentActions;
|
|
273
|
+
readonly providers: OttoProviderActions;
|
|
274
|
+
readonly config: OttoConfigActions;
|
|
275
|
+
connect(): Promise<void>;
|
|
276
|
+
close(): Promise<void>;
|
|
277
|
+
ensureConnected(): void;
|
|
278
|
+
getConnectionState(): ConnectionState;
|
|
279
|
+
}
|
|
280
|
+
export declare function createOttoClient(config: OttoClientConfig): OttoClient;
|
|
281
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { DaemonClient } from "./daemon-client.js";
|
|
2
|
+
export { DaemonClient };
|
|
3
|
+
export function createOttoClient(config) {
|
|
4
|
+
const daemonClient = new DaemonClient({
|
|
5
|
+
...config,
|
|
6
|
+
clientId: config.clientId ?? createGeneratedClientId(),
|
|
7
|
+
clientType: "cli",
|
|
8
|
+
});
|
|
9
|
+
const createWorkspaceHandle = createWorkspaceHandleFactory(daemonClient);
|
|
10
|
+
const createAgentHandle = createAgentHandleFactory(daemonClient);
|
|
11
|
+
return {
|
|
12
|
+
workspaces: {
|
|
13
|
+
list: (options) => daemonClient.fetchWorkspaces(options),
|
|
14
|
+
ref: (workspace) => createWorkspaceHandle(workspace),
|
|
15
|
+
open: (input, requestId) => openWorkspace(daemonClient, createWorkspaceHandle, input, requestId),
|
|
16
|
+
create: (input, requestId) => openWorkspace(daemonClient, createWorkspaceHandle, input, requestId),
|
|
17
|
+
archive: (workspace, requestId) => daemonClient.archiveWorkspace(resolveWorkspaceId(workspace), requestId),
|
|
18
|
+
subscribe: (handler) => daemonClient.on("workspace_update", (message) => {
|
|
19
|
+
handler(message.payload);
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
agents: {
|
|
23
|
+
ref: (agent) => createAgentHandle(agent),
|
|
24
|
+
create: async (options) => {
|
|
25
|
+
const agent = await daemonClient.createAgent(options);
|
|
26
|
+
return createAgentHandle(agent);
|
|
27
|
+
},
|
|
28
|
+
subscribe: (handler) => daemonClient.on("agent_update", (message) => {
|
|
29
|
+
handler(message.payload);
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
providers: {
|
|
33
|
+
codex: (input) => providerConfig("codex", input),
|
|
34
|
+
claude: (input) => providerConfig("claude", input),
|
|
35
|
+
opencode: (input) => providerConfig("opencode", input),
|
|
36
|
+
copilot: (input) => providerConfig("copilot", input),
|
|
37
|
+
config: (provider, input) => providerConfig(provider, input),
|
|
38
|
+
listModels: (provider, options) => daemonClient.listProviderModels(provider, options),
|
|
39
|
+
listModes: (provider, options) => daemonClient.listProviderModes(provider, options),
|
|
40
|
+
listFeatures: (draftConfig, options) => daemonClient.listProviderFeatures(draftConfig, options),
|
|
41
|
+
listAvailable: (options) => daemonClient.listAvailableProviders(options),
|
|
42
|
+
snapshot: (options) => daemonClient.getProvidersSnapshot(options),
|
|
43
|
+
refresh: (options) => daemonClient.refreshProvidersSnapshot(options),
|
|
44
|
+
diagnostic: (provider, options) => daemonClient.getProviderDiagnostic(provider, options),
|
|
45
|
+
subscribe: (handler) => daemonClient.on("providers_snapshot_update", (message) => {
|
|
46
|
+
handler(message.payload);
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
config: {
|
|
50
|
+
get: (requestId) => daemonClient.getDaemonConfig(requestId),
|
|
51
|
+
patch: (patch, requestId) => daemonClient.patchDaemonConfig(patch, requestId),
|
|
52
|
+
},
|
|
53
|
+
connect: () => daemonClient.connect(),
|
|
54
|
+
close: () => daemonClient.close(),
|
|
55
|
+
ensureConnected: () => daemonClient.ensureConnected(),
|
|
56
|
+
getConnectionState: () => daemonClient.getConnectionState(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function createWorkspaceHandleFactory(daemonClient) {
|
|
60
|
+
return (workspace) => {
|
|
61
|
+
const id = typeof workspace === "string" ? workspace : workspace.id;
|
|
62
|
+
let latest = typeof workspace === "string" ? null : workspace;
|
|
63
|
+
return {
|
|
64
|
+
id,
|
|
65
|
+
latest: () => latest,
|
|
66
|
+
refetch: async (options) => {
|
|
67
|
+
// Best-effort: fetches one page and matches by id client-side, so a workspace beyond
|
|
68
|
+
// the first page won't be found. TODO: add a "get workspace by id" lookup and resolve
|
|
69
|
+
// by exact id instead of paging.
|
|
70
|
+
const result = await daemonClient.fetchWorkspaces({
|
|
71
|
+
requestId: options?.requestId,
|
|
72
|
+
page: { limit: 25 },
|
|
73
|
+
});
|
|
74
|
+
latest = result.entries.find((entry) => entry.id === id) ?? null;
|
|
75
|
+
return latest;
|
|
76
|
+
},
|
|
77
|
+
archive: async (requestId) => {
|
|
78
|
+
const result = await daemonClient.archiveWorkspace(id, requestId);
|
|
79
|
+
if (latest) {
|
|
80
|
+
latest = { ...latest, archivingAt: result.archivedAt };
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
},
|
|
84
|
+
subscribe: (handler) => daemonClient.on("workspace_update", (message) => {
|
|
85
|
+
const update = message.payload;
|
|
86
|
+
if (update.kind === "upsert" && update.workspace.id === id) {
|
|
87
|
+
latest = update.workspace;
|
|
88
|
+
handler(update);
|
|
89
|
+
}
|
|
90
|
+
if (update.kind === "remove" && update.id === id) {
|
|
91
|
+
latest = null;
|
|
92
|
+
handler(update);
|
|
93
|
+
}
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function createAgentHandleFactory(daemonClient) {
|
|
99
|
+
return (agent) => {
|
|
100
|
+
const id = typeof agent === "string" ? agent : agent.id;
|
|
101
|
+
let latest = typeof agent === "string" ? null : agent;
|
|
102
|
+
const handle = {
|
|
103
|
+
id,
|
|
104
|
+
timeline: {
|
|
105
|
+
refetch: async (options) => {
|
|
106
|
+
const result = await daemonClient.fetchAgentTimeline(id, options);
|
|
107
|
+
if (result.agent) {
|
|
108
|
+
latest = result.agent;
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
},
|
|
112
|
+
subscribe: (handler) => daemonClient.on("agent_stream", (message) => {
|
|
113
|
+
if (message.payload.agentId === id) {
|
|
114
|
+
handler(message.payload);
|
|
115
|
+
}
|
|
116
|
+
}),
|
|
117
|
+
},
|
|
118
|
+
latest: () => latest,
|
|
119
|
+
refetch: async (requestId) => {
|
|
120
|
+
const result = await daemonClient.fetchAgent({ agentId: id, requestId });
|
|
121
|
+
latest = result?.agent ?? null;
|
|
122
|
+
return result;
|
|
123
|
+
},
|
|
124
|
+
send: (text, options) => daemonClient.sendAgentMessage(id, text, options),
|
|
125
|
+
archive: async () => {
|
|
126
|
+
const result = await daemonClient.archiveAgent(id);
|
|
127
|
+
if (latest) {
|
|
128
|
+
latest = { ...latest, archivedAt: result.archivedAt };
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
},
|
|
132
|
+
detach: async () => {
|
|
133
|
+
await daemonClient.detachAgent(id);
|
|
134
|
+
},
|
|
135
|
+
subscribe: (handler) => daemonClient.on("agent_update", (message) => {
|
|
136
|
+
const update = message.payload;
|
|
137
|
+
if (update.kind === "upsert" && update.agent.id === id) {
|
|
138
|
+
latest = update.agent;
|
|
139
|
+
handler(update);
|
|
140
|
+
}
|
|
141
|
+
if (update.kind === "remove" && update.agentId === id) {
|
|
142
|
+
latest = null;
|
|
143
|
+
handler(update);
|
|
144
|
+
}
|
|
145
|
+
}),
|
|
146
|
+
};
|
|
147
|
+
return handle;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
async function openWorkspace(daemonClient, createWorkspaceHandle, input, requestId) {
|
|
151
|
+
const options = typeof input === "string" ? { cwd: input, requestId } : input;
|
|
152
|
+
const result = await daemonClient.openProject(options.cwd, options.requestId);
|
|
153
|
+
return {
|
|
154
|
+
...result,
|
|
155
|
+
workspace: result.workspace ? createWorkspaceHandle(result.workspace) : null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function resolveWorkspaceId(workspace) {
|
|
159
|
+
return typeof workspace === "string" ? workspace : workspace.id;
|
|
160
|
+
}
|
|
161
|
+
function providerConfig(provider, input = {}) {
|
|
162
|
+
return {
|
|
163
|
+
provider,
|
|
164
|
+
...(input.model !== undefined ? { model: input.model } : {}),
|
|
165
|
+
...(input.modeId !== undefined ? { modeId: input.modeId } : {}),
|
|
166
|
+
...(input.thinkingOptionId !== undefined ? { thinkingOptionId: input.thinkingOptionId } : {}),
|
|
167
|
+
...(input.featureValues !== undefined ? { featureValues: input.featureValues } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createGeneratedClientId() {
|
|
171
|
+
const randomId = typeof globalThis.crypto?.randomUUID === "function"
|
|
172
|
+
? globalThis.crypto.randomUUID()
|
|
173
|
+
: Math.random().toString(36).slice(2);
|
|
174
|
+
return `otto-sdk-${randomId}`;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type TerminalStreamFrame } from "@otto-code/protocol/binary-frames/index";
|
|
2
|
+
import type { TerminalInput, TerminalState } from "@otto-code/protocol/messages";
|
|
3
|
+
export type TerminalStreamEvent = {
|
|
4
|
+
terminalId: string;
|
|
5
|
+
type: "output";
|
|
6
|
+
data: Uint8Array;
|
|
7
|
+
} | {
|
|
8
|
+
terminalId: string;
|
|
9
|
+
type: "snapshot";
|
|
10
|
+
state: TerminalState;
|
|
11
|
+
} | {
|
|
12
|
+
terminalId: string;
|
|
13
|
+
type: "restore";
|
|
14
|
+
data: Uint8Array;
|
|
15
|
+
};
|
|
16
|
+
export declare class TerminalStreamRouter {
|
|
17
|
+
private readonly terminalSlots;
|
|
18
|
+
private readonly slotTerminals;
|
|
19
|
+
private readonly listeners;
|
|
20
|
+
onEvent(handler: (event: TerminalStreamEvent) => void): () => void;
|
|
21
|
+
setSlot(terminalId: string, slot: number): void;
|
|
22
|
+
removeTerminal(terminalId: string): void;
|
|
23
|
+
clearSlots(): void;
|
|
24
|
+
encodeInput(terminalId: string, message: TerminalInput["message"]): Uint8Array | null;
|
|
25
|
+
handleFrame(frame: TerminalStreamFrame): void;
|
|
26
|
+
private emit;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=terminal-stream-router.d.ts.map
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { decodeTerminalSnapshotPayload, encodeTerminalResizePayload, encodeTerminalStreamFrame, TerminalStreamOpcode, } from "@otto-code/protocol/binary-frames/index";
|
|
2
|
+
export class TerminalStreamRouter {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.terminalSlots = new Map();
|
|
5
|
+
this.slotTerminals = new Map();
|
|
6
|
+
this.listeners = new Set();
|
|
7
|
+
}
|
|
8
|
+
onEvent(handler) {
|
|
9
|
+
this.listeners.add(handler);
|
|
10
|
+
return () => {
|
|
11
|
+
this.listeners.delete(handler);
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
setSlot(terminalId, slot) {
|
|
15
|
+
const existingTerminalId = this.slotTerminals.get(slot);
|
|
16
|
+
if (existingTerminalId && existingTerminalId !== terminalId) {
|
|
17
|
+
this.terminalSlots.delete(existingTerminalId);
|
|
18
|
+
}
|
|
19
|
+
const existingSlot = this.terminalSlots.get(terminalId);
|
|
20
|
+
if (typeof existingSlot === "number" && existingSlot !== slot) {
|
|
21
|
+
this.slotTerminals.delete(existingSlot);
|
|
22
|
+
}
|
|
23
|
+
this.terminalSlots.set(terminalId, slot);
|
|
24
|
+
this.slotTerminals.set(slot, terminalId);
|
|
25
|
+
}
|
|
26
|
+
removeTerminal(terminalId) {
|
|
27
|
+
const slot = this.terminalSlots.get(terminalId);
|
|
28
|
+
if (typeof slot !== "number") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.terminalSlots.delete(terminalId);
|
|
32
|
+
if (this.slotTerminals.get(slot) === terminalId) {
|
|
33
|
+
this.slotTerminals.delete(slot);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
clearSlots() {
|
|
37
|
+
this.terminalSlots.clear();
|
|
38
|
+
this.slotTerminals.clear();
|
|
39
|
+
}
|
|
40
|
+
encodeInput(terminalId, message) {
|
|
41
|
+
const slot = this.terminalSlots.get(terminalId);
|
|
42
|
+
if (typeof slot !== "number") {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
if (message.type === "input") {
|
|
46
|
+
return encodeTerminalStreamFrame({
|
|
47
|
+
opcode: TerminalStreamOpcode.Input,
|
|
48
|
+
slot,
|
|
49
|
+
payload: message.data,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (message.type === "resize") {
|
|
53
|
+
return encodeTerminalStreamFrame({
|
|
54
|
+
opcode: TerminalStreamOpcode.Resize,
|
|
55
|
+
slot,
|
|
56
|
+
payload: encodeTerminalResizePayload({
|
|
57
|
+
rows: message.rows,
|
|
58
|
+
cols: message.cols,
|
|
59
|
+
}),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
handleFrame(frame) {
|
|
65
|
+
const terminalId = this.slotTerminals.get(frame.slot);
|
|
66
|
+
if (!terminalId) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (frame.opcode === TerminalStreamOpcode.Output) {
|
|
70
|
+
this.emit({
|
|
71
|
+
terminalId,
|
|
72
|
+
type: "output",
|
|
73
|
+
data: frame.payload,
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (frame.opcode === TerminalStreamOpcode.Restore) {
|
|
78
|
+
this.emit({
|
|
79
|
+
terminalId,
|
|
80
|
+
type: "restore",
|
|
81
|
+
data: frame.payload,
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (frame.opcode === TerminalStreamOpcode.Snapshot) {
|
|
86
|
+
const state = decodeTerminalSnapshotPayload(frame.payload);
|
|
87
|
+
if (!state) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.emit({
|
|
91
|
+
terminalId,
|
|
92
|
+
type: "snapshot",
|
|
93
|
+
state,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
emit(event) {
|
|
98
|
+
for (const listener of this.listeners) {
|
|
99
|
+
try {
|
|
100
|
+
listener(event);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// no-op
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=terminal-stream-router.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@otto-code/client",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Otto client SDK package",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist",
|
|
7
|
+
"!dist/**/*.map",
|
|
8
|
+
"README.md"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./internal/daemon-client": {
|
|
17
|
+
"types": "./dist/daemon-client.d.ts",
|
|
18
|
+
"default": "./dist/daemon-client.js"
|
|
19
|
+
},
|
|
20
|
+
"./internal/daemon-client-transport-types": {
|
|
21
|
+
"types": "./dist/daemon-client-transport-types.d.ts",
|
|
22
|
+
"default": "./dist/daemon-client-transport-types.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"clean": "node ../../scripts/clean-package-dist.mjs",
|
|
30
|
+
"build": "npm run build --workspace=@otto-code/protocol && tsc -p tsconfig.json --incremental false",
|
|
31
|
+
"build:clean": "npm run clean && npm run build",
|
|
32
|
+
"prepack": "npm run build:clean",
|
|
33
|
+
"typecheck": "tsgo --noEmit",
|
|
34
|
+
"typecheck:examples": "tsgo -p tsconfig.examples.json --noEmit",
|
|
35
|
+
"test": "vitest run"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@otto-code/protocol": "0.5.0",
|
|
39
|
+
"@otto-code/relay": "0.5.0",
|
|
40
|
+
"zod": "^4.4.3"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/node": "^20.9.0",
|
|
44
|
+
"typescript": "^5.2.2",
|
|
45
|
+
"vitest": "^4.1.6"
|
|
46
|
+
}
|
|
47
|
+
}
|