@factory/droid-sdk 0.3.0 → 0.4.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/dist/index.js CHANGED
@@ -255,6 +255,11 @@ var JsonRpcMessageType = /* @__PURE__ */ ((JsonRpcMessageType2) => {
255
255
  JsonRpcMessageType2["Notification"] = "notification";
256
256
  return JsonRpcMessageType2;
257
257
  })(JsonRpcMessageType || {});
258
+ var ServerRequestHandlerType = /* @__PURE__ */ ((ServerRequestHandlerType2) => {
259
+ ServerRequestHandlerType2["Permission"] = "permission";
260
+ ServerRequestHandlerType2["AskUser"] = "askUser";
261
+ return ServerRequestHandlerType2;
262
+ })(ServerRequestHandlerType || {});
258
263
  var SettingsLevel = /* @__PURE__ */ ((SettingsLevel2) => {
259
264
  SettingsLevel2["Org"] = "org";
260
265
  SettingsLevel2["Runtime"] = "runtime";
@@ -275,7 +280,7 @@ var SkillLocation = /* @__PURE__ */ ((SkillLocation2) => {
275
280
  // src/schemas/constants.ts
276
281
  var JSONRPC_VERSION = "2.0";
277
282
  var LEGACY_FACTORY_API_VERSION = "1.0.0";
278
- var FACTORY_PROTOCOL_VERSION = "1.3.0";
283
+ var FACTORY_PROTOCOL_VERSION = "1.51.0";
279
284
  var FACTORY_CLIENT_HEADER = "X-Factory-Client";
280
285
  var FACTORY_CLIENT_VERSION = "X-Client-Version";
281
286
  var DEFAULT_REQUEST_TIMEOUT = 3e4;
@@ -1135,6 +1140,19 @@ var ForkSessionResultSchema = z5.object({
1135
1140
  newSessionId: z5.string()
1136
1141
  }).passthrough();
1137
1142
  var GetContextStatsResultSchema = ContextStatsSchema;
1143
+ var ContextBreakdownCategorySchema = z5.object({
1144
+ name: z5.string(),
1145
+ tokens: z5.number(),
1146
+ colorKey: z5.string()
1147
+ }).passthrough();
1148
+ var ContextBreakdownResultSchema = z5.object({
1149
+ modelId: z5.string(),
1150
+ modelDisplayName: z5.string(),
1151
+ contextBudget: z5.number(),
1152
+ usedTokens: z5.number(),
1153
+ freeTokens: z5.number(),
1154
+ categories: z5.array(ContextBreakdownCategorySchema)
1155
+ }).passthrough();
1138
1156
  var RenameSessionResultSchema = SuccessResultSchema;
1139
1157
  var InitializeSessionResponseSchema = createResponseSchema(
1140
1158
  InitializeSessionResultSchema
@@ -1638,7 +1656,7 @@ var _CliRequestOrNotificationSchema = z8.union([
1638
1656
  var CliRequestOrNotificationSchema = _CliRequestOrNotificationSchema;
1639
1657
 
1640
1658
  // src/constants.ts
1641
- var SDK_VERSION = "0.3.0";
1659
+ var SDK_VERSION = "0.4.0";
1642
1660
  var SDK_TAG = {
1643
1661
  name: "sdk",
1644
1662
  metadata: { language: "typescript", version: SDK_VERSION }
@@ -1972,9 +1990,14 @@ function dispatchNotification(notification, listeners) {
1972
1990
  }
1973
1991
  }
1974
1992
  }
1993
+ var DEFAULT_SERVER_REQUEST_METHOD_MAP = {
1994
+ ["droid.request_permission" /* REQUEST_PERMISSION */]: "permission" /* Permission */,
1995
+ ["droid.ask_user" /* ASK_USER */]: "askUser" /* AskUser */
1996
+ };
1975
1997
  var ProtocolEngine = class {
1976
1998
  _transport;
1977
1999
  _defaultTimeout;
2000
+ _serverRequestMethodMap;
1978
2001
  _pendingRequests = /* @__PURE__ */ new Map();
1979
2002
  _notificationListeners = /* @__PURE__ */ new Set();
1980
2003
  _permissionHandler = null;
@@ -1984,6 +2007,7 @@ var ProtocolEngine = class {
1984
2007
  constructor(options) {
1985
2008
  this._transport = options.transport;
1986
2009
  this._defaultTimeout = options.defaultTimeout ?? DEFAULT_REQUEST_TIMEOUT;
2010
+ this._serverRequestMethodMap = options.serverRequestMethodMap ?? DEFAULT_SERVER_REQUEST_METHOD_MAP;
1987
2011
  this._transport.onMessage((message) => {
1988
2012
  this._handleMessage(message);
1989
2013
  });
@@ -2139,9 +2163,10 @@ var ProtocolEngine = class {
2139
2163
  dispatchNotification(notification, this._notificationListeners);
2140
2164
  }
2141
2165
  async _handleServerRequest(method, requestId, params) {
2142
- if (method === "droid.request_permission" /* REQUEST_PERMISSION */) {
2166
+ const handlerType = this._serverRequestMethodMap[method];
2167
+ if (handlerType === "permission" /* Permission */) {
2143
2168
  await this._handlePermissionRequest(requestId, params);
2144
- } else if (method === "droid.ask_user" /* ASK_USER */) {
2169
+ } else if (handlerType === "askUser" /* AskUser */) {
2145
2170
  await this._handleAskUserRequest(requestId, params);
2146
2171
  }
2147
2172
  }
@@ -2999,6 +3024,69 @@ function wireAbortSignal(signal, onAbort) {
2999
3024
  return () => signal.removeEventListener("abort", listener);
3000
3025
  }
3001
3026
  }
3027
+ function getAbortError(signal) {
3028
+ if (signal.reason instanceof Error) {
3029
+ return signal.reason;
3030
+ }
3031
+ return new Error(
3032
+ typeof signal.reason === "string" ? signal.reason : "Operation aborted"
3033
+ );
3034
+ }
3035
+ function throwIfAborted(signal) {
3036
+ if (signal?.aborted) {
3037
+ throw getAbortError(signal);
3038
+ }
3039
+ }
3040
+ async function* streamFromClient(client, sessionId, activeBridges, prompt, options) {
3041
+ throwIfAborted(options?.abortSignal);
3042
+ const startedAt = Date.now();
3043
+ let resolveDone = () => {
3044
+ };
3045
+ const donePromise = new Promise((resolve2) => {
3046
+ resolveDone = resolve2;
3047
+ });
3048
+ const bridge = new MessageBridge(resolveDone, {
3049
+ includePartialMessages: options?.includePartialMessages,
3050
+ sessionId,
3051
+ startedAt,
3052
+ outputFormat: options?.outputFormat
3053
+ });
3054
+ activeBridges.add(bridge);
3055
+ const unsubscribe = client.onNotification(bridge.notificationHandler);
3056
+ let resolveAbort = () => {
3057
+ };
3058
+ const abortPromise = new Promise((resolve2) => {
3059
+ resolveAbort = resolve2;
3060
+ });
3061
+ const cleanupAbortSignal = wireAbortSignal(options?.abortSignal, () => {
3062
+ bridge.signalDone();
3063
+ resolveAbort();
3064
+ void client.interruptSession().catch(() => {
3065
+ });
3066
+ });
3067
+ try {
3068
+ await Promise.race([
3069
+ client.addUserMessage({
3070
+ text: prompt,
3071
+ images: options?.images,
3072
+ files: options?.files,
3073
+ outputFormat: options?.outputFormat
3074
+ }),
3075
+ donePromise,
3076
+ abortPromise
3077
+ ]);
3078
+ throwIfAborted(options?.abortSignal);
3079
+ for await (const msg of bridge.messages()) {
3080
+ throwIfAborted(options?.abortSignal);
3081
+ yield msg;
3082
+ }
3083
+ throwIfAborted(options?.abortSignal);
3084
+ } finally {
3085
+ cleanupAbortSignal();
3086
+ unsubscribe();
3087
+ activeBridges.delete(bridge);
3088
+ }
3089
+ }
3002
3090
  function extractInnerNotification(notification) {
3003
3091
  const parsed = SessionNotificationSchema.safeParse(notification);
3004
3092
  return parsed.success ? parsed.data.params.notification : null;
@@ -3094,7 +3182,7 @@ async function createTransport(options) {
3094
3182
  execPath: options.execPath,
3095
3183
  execArgs: options.execArgs,
3096
3184
  cwd: options.cwd,
3097
- env: options.env
3185
+ env: { ...options.env, FACTORY_API_KEY: options.apiKey }
3098
3186
  };
3099
3187
  const processTransport = new ProcessTransport(transportOptions);
3100
3188
  await processTransport.connect();
@@ -3152,6 +3240,9 @@ function buildInitParams(options) {
3152
3240
  ...options.disabledToolIds !== void 0 && {
3153
3241
  disabledToolIds: options.disabledToolIds
3154
3242
  },
3243
+ ...options.sessionSource !== void 0 && {
3244
+ sessionSource: options.sessionSource
3245
+ },
3155
3246
  tags: [...options.tags ?? [], SDK_TAG]
3156
3247
  };
3157
3248
  }
@@ -3352,19 +3443,6 @@ async function startSdkMcpServers(servers) {
3352
3443
  }
3353
3444
 
3354
3445
  // src/session.ts
3355
- function getAbortError(signal) {
3356
- if (signal.reason instanceof Error) {
3357
- return signal.reason;
3358
- }
3359
- return new Error(
3360
- typeof signal.reason === "string" ? signal.reason : "Operation aborted"
3361
- );
3362
- }
3363
- function throwIfAborted(signal) {
3364
- if (signal?.aborted) {
3365
- throw getAbortError(signal);
3366
- }
3367
- }
3368
3446
  var DroidSession = class {
3369
3447
  _client;
3370
3448
  _sessionId;
@@ -3395,54 +3473,13 @@ var DroidSession = class {
3395
3473
  }
3396
3474
  async *stream(prompt, options) {
3397
3475
  this._ensureNotClosed();
3398
- throwIfAborted(options?.abortSignal);
3399
- const startedAt = Date.now();
3400
- let resolveDone = () => {
3401
- };
3402
- const donePromise = new Promise((resolve2) => {
3403
- resolveDone = resolve2;
3404
- });
3405
- const bridge = new MessageBridge(resolveDone, {
3406
- includePartialMessages: options?.includePartialMessages,
3407
- sessionId: this._sessionId,
3408
- startedAt,
3409
- outputFormat: options?.outputFormat
3410
- });
3411
- this._activeBridges.add(bridge);
3412
- const unsubscribe = this._client.onNotification(bridge.notificationHandler);
3413
- let resolveAbort = () => {
3414
- };
3415
- const abortPromise = new Promise((resolve2) => {
3416
- resolveAbort = resolve2;
3417
- });
3418
- const cleanupAbortSignal = wireAbortSignal(options?.abortSignal, () => {
3419
- bridge.signalDone();
3420
- resolveAbort();
3421
- void this._client.interruptSession().catch(() => {
3422
- });
3423
- });
3424
- try {
3425
- await Promise.race([
3426
- this._client.addUserMessage({
3427
- text: prompt,
3428
- images: options?.images,
3429
- files: options?.files,
3430
- outputFormat: options?.outputFormat
3431
- }),
3432
- donePromise,
3433
- abortPromise
3434
- ]);
3435
- throwIfAborted(options?.abortSignal);
3436
- for await (const msg of bridge.messages()) {
3437
- throwIfAborted(options?.abortSignal);
3438
- yield msg;
3439
- }
3440
- throwIfAborted(options?.abortSignal);
3441
- } finally {
3442
- cleanupAbortSignal();
3443
- unsubscribe();
3444
- this._activeBridges.delete(bridge);
3445
- }
3476
+ yield* streamFromClient(
3477
+ this._client,
3478
+ this._sessionId,
3479
+ this._activeBridges,
3480
+ prompt,
3481
+ options
3482
+ );
3446
3483
  }
3447
3484
  async interrupt() {
3448
3485
  this._ensureNotClosed();
@@ -3545,7 +3582,7 @@ var DroidSession = class {
3545
3582
  }
3546
3583
  }
3547
3584
  };
3548
- async function createSession(options = {}) {
3585
+ async function createSession(options) {
3549
3586
  const { client } = await createConfiguredClient(options);
3550
3587
  let cleanupInitAbortSignal = options.abortSignal?.aborted ? () => {
3551
3588
  } : wireAbortSignal(options.abortSignal, () => {
@@ -3575,7 +3612,7 @@ async function createSession(options = {}) {
3575
3612
  throw error;
3576
3613
  }
3577
3614
  }
3578
- async function resumeSession(sessionId, options = {}) {
3615
+ async function resumeSession(sessionId, options) {
3579
3616
  const { client } = await createConfiguredClient(options);
3580
3617
  let sdkMcpServers;
3581
3618
  try {
@@ -3599,7 +3636,7 @@ async function resumeSession(sessionId, options = {}) {
3599
3636
  }
3600
3637
 
3601
3638
  // src/run.ts
3602
- async function run(prompt, options = {}) {
3639
+ async function run(prompt, options) {
3603
3640
  const session = await createSession(options);
3604
3641
  try {
3605
3642
  for await (const msg of session.stream(prompt, {
@@ -3839,6 +3876,1431 @@ async function listSessions(options = {}) {
3839
3876
  }
3840
3877
  return sorted;
3841
3878
  }
3879
+
3880
+ // src/api-types.ts
3881
+ import { z as z10 } from "zod";
3882
+ var EnvironmentVariableSchema = z10.object({
3883
+ key: z10.string(),
3884
+ value: z10.string()
3885
+ });
3886
+ var MachineTemplateBuildStatusSchema = z10.object({
3887
+ status: z10.enum(["building", "success", "failed"]),
3888
+ failureReason: z10.enum(["setup_script_error", "system_error"]).optional(),
3889
+ buildStartedAt: z10.number().int().optional(),
3890
+ builtAt: z10.number().int().optional(),
3891
+ logs: z10.string().optional()
3892
+ });
3893
+ var MachineTemplateSchema = z10.object({
3894
+ templateId: z10.string(),
3895
+ repoUrl: z10.string(),
3896
+ templateName: z10.string(),
3897
+ defaultBranch: z10.string(),
3898
+ createdBy: z10.string(),
3899
+ createdAt: z10.number().int().optional(),
3900
+ buildStatus: MachineTemplateBuildStatusSchema.optional(),
3901
+ lastUpdatedAt: z10.number().int().nullable().optional(),
3902
+ environmentVariables: z10.array(EnvironmentVariableSchema).optional(),
3903
+ userEnvironmentVariablesByUser: z10.array(EnvironmentVariableSchema).optional(),
3904
+ setupScript: z10.string().optional()
3905
+ });
3906
+ var MachineTemplateListResponseSchema = z10.object({
3907
+ templates: z10.array(MachineTemplateSchema),
3908
+ pagination: z10.object({
3909
+ hasMore: z10.boolean(),
3910
+ nextCursor: z10.string().nullable()
3911
+ })
3912
+ });
3913
+ var ComputerSchema = z10.object({
3914
+ id: z10.string(),
3915
+ name: z10.string(),
3916
+ hostname: z10.string().optional(),
3917
+ providerType: z10.enum(["byom", "e2b"]),
3918
+ status: z10.enum(["provisioning", "active", "error"]).optional(),
3919
+ createdAt: z10.number().int(),
3920
+ relayClientUrl: z10.string().optional(),
3921
+ remoteUser: z10.string().optional()
3922
+ }).passthrough();
3923
+ var ComputerListResponseSchema = z10.object({
3924
+ computers: z10.array(ComputerSchema)
3925
+ });
3926
+ var RefreshComputerResponseSchema = z10.object({
3927
+ configured: z10.number().int()
3928
+ });
3929
+ var ComputerMetricSchema = z10.object({
3930
+ timestamp: z10.string(),
3931
+ cpuUsedPct: z10.number(),
3932
+ cpuCount: z10.number(),
3933
+ memUsed: z10.number(),
3934
+ memTotal: z10.number(),
3935
+ diskUsed: z10.number(),
3936
+ diskTotal: z10.number()
3937
+ });
3938
+ var ComputerMetricsResponseSchema = z10.array(ComputerMetricSchema);
3939
+ var RemoteSessionSchema = z10.object({
3940
+ sessionId: z10.string(),
3941
+ title: z10.string().optional(),
3942
+ status: z10.enum(["idle", "pending", "running"]),
3943
+ messageCount: z10.number().int(),
3944
+ createdAt: z10.number().int(),
3945
+ updatedAt: z10.number().int(),
3946
+ completedAt: z10.number().int().optional(),
3947
+ computerId: z10.string().optional()
3948
+ });
3949
+ var RemoteSessionListResponseSchema = z10.object({
3950
+ sessions: z10.array(RemoteSessionSchema),
3951
+ pagination: z10.object({
3952
+ hasMore: z10.boolean(),
3953
+ nextCursor: z10.string().nullable()
3954
+ })
3955
+ });
3956
+
3957
+ // src/api.ts
3958
+ var DEFAULT_BASE_URL = "https://api.factory.ai";
3959
+ function extractErrorMessage(body, status) {
3960
+ if (isRecord(body) && typeof body["error"] === "string") {
3961
+ return body["error"];
3962
+ }
3963
+ return `Factory API error (status ${status})`;
3964
+ }
3965
+ async function factoryFetch(path3, apiKey, baseUrl, options) {
3966
+ const url = `${baseUrl ?? DEFAULT_BASE_URL}${path3}`;
3967
+ const method = options?.method ?? "GET";
3968
+ const headers = {
3969
+ Authorization: `Bearer ${apiKey}`,
3970
+ Accept: "application/json"
3971
+ };
3972
+ if (options?.body) {
3973
+ headers["Content-Type"] = "application/json";
3974
+ }
3975
+ let response;
3976
+ try {
3977
+ response = await fetch(url, {
3978
+ method,
3979
+ headers,
3980
+ ...options?.body ? { body: JSON.stringify(options.body) } : {}
3981
+ });
3982
+ } catch (error) {
3983
+ throw new ConnectionError(
3984
+ `Failed to reach Factory API: ${error instanceof Error ? error.message : String(error)}`
3985
+ );
3986
+ }
3987
+ if (response.status === 204) {
3988
+ return void 0;
3989
+ }
3990
+ let body;
3991
+ try {
3992
+ body = await response.json();
3993
+ } catch {
3994
+ throw new ProtocolError(
3995
+ `Factory API returned non-JSON response (status ${response.status})`
3996
+ );
3997
+ }
3998
+ if (!response.ok) {
3999
+ const message = extractErrorMessage(body, response.status);
4000
+ if (response.status === 401 || response.status === 403) {
4001
+ throw new ProtocolError(`Invalid or expired API key: ${message}`, {
4002
+ code: response.status
4003
+ });
4004
+ }
4005
+ throw new ProtocolError(message, { code: response.status });
4006
+ }
4007
+ return body;
4008
+ }
4009
+ function parseResponse(body, schema) {
4010
+ const parsed = schema.safeParse(body);
4011
+ if (!parsed.success) {
4012
+ throw new ProtocolError(
4013
+ `Unexpected response format from Factory API: ${parsed.error.message}`
4014
+ );
4015
+ }
4016
+ return parsed.data;
4017
+ }
4018
+ async function listMachineTemplates(options) {
4019
+ const params = new URLSearchParams();
4020
+ if (options.limit != null) {
4021
+ params.set("limit", String(options.limit));
4022
+ }
4023
+ if (options.cursor != null) {
4024
+ params.set("cursor", options.cursor);
4025
+ }
4026
+ const query = params.toString();
4027
+ const path3 = `/api/v0/machines/templates${query ? `?${query}` : ""}`;
4028
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4029
+ return parseResponse(body, MachineTemplateListResponseSchema);
4030
+ }
4031
+ async function getMachineTemplate(options) {
4032
+ const path3 = `/api/v0/machines/templates/${encodeURIComponent(options.templateId)}`;
4033
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4034
+ return parseResponse(body, MachineTemplateSchema);
4035
+ }
4036
+ async function listComputers(options) {
4037
+ const path3 = "/api/v0/computers";
4038
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4039
+ return parseResponse(body, ComputerListResponseSchema);
4040
+ }
4041
+ async function getComputer(options) {
4042
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}`;
4043
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4044
+ return parseResponse(body, ComputerSchema);
4045
+ }
4046
+ async function createComputer(options) {
4047
+ const path3 = "/api/v0/computers";
4048
+ const reqBody = {
4049
+ name: options.name,
4050
+ remoteUser: options.remoteUser
4051
+ };
4052
+ if (options.provider != null) reqBody["provider"] = options.provider;
4053
+ if (options.hostId != null) reqBody["hostId"] = options.hostId;
4054
+ if (options.repos != null) reqBody["repos"] = options.repos;
4055
+ if (options.autoInstallDeps != null)
4056
+ reqBody["autoInstallDeps"] = options.autoInstallDeps;
4057
+ if (options.serviceAccountId != null)
4058
+ reqBody["serviceAccountId"] = options.serviceAccountId;
4059
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl, {
4060
+ method: "POST",
4061
+ body: reqBody
4062
+ });
4063
+ return parseResponse(body, ComputerSchema);
4064
+ }
4065
+ async function getComputerByName(options) {
4066
+ const path3 = `/api/v0/computers/name/${encodeURIComponent(options.name)}`;
4067
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4068
+ return parseResponse(body, ComputerSchema);
4069
+ }
4070
+ async function updateComputer(options) {
4071
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}`;
4072
+ const reqBody = {};
4073
+ if (options.name != null) reqBody["name"] = options.name;
4074
+ if (options.remoteUser != null) reqBody["remoteUser"] = options.remoteUser;
4075
+ if (options.hostId != null) reqBody["hostId"] = options.hostId;
4076
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl, {
4077
+ method: "PATCH",
4078
+ body: reqBody
4079
+ });
4080
+ return parseResponse(body, ComputerSchema);
4081
+ }
4082
+ async function deleteComputer(options) {
4083
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}`;
4084
+ await factoryFetch(path3, options.apiKey, options.baseUrl, {
4085
+ method: "DELETE"
4086
+ });
4087
+ }
4088
+ async function restartComputer(options) {
4089
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}/restart`;
4090
+ await factoryFetch(path3, options.apiKey, options.baseUrl, {
4091
+ method: "POST"
4092
+ });
4093
+ }
4094
+ async function refreshComputer(options) {
4095
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}/refresh`;
4096
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl, {
4097
+ method: "POST"
4098
+ });
4099
+ return parseResponse(body, RefreshComputerResponseSchema);
4100
+ }
4101
+ async function getComputerMetrics(options) {
4102
+ const params = new URLSearchParams();
4103
+ if (options.start != null) {
4104
+ params.set("start", options.start);
4105
+ }
4106
+ const query = params.toString();
4107
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}/metrics${query ? `?${query}` : ""}`;
4108
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4109
+ return parseResponse(body, ComputerMetricsResponseSchema);
4110
+ }
4111
+ async function retryInstallDeps(options) {
4112
+ const path3 = `/api/v0/computers/${encodeURIComponent(options.computerId)}/install-deps`;
4113
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl, {
4114
+ method: "POST"
4115
+ });
4116
+ return parseResponse(body, ComputerSchema);
4117
+ }
4118
+ async function listRemoteSessions(options) {
4119
+ const params = new URLSearchParams();
4120
+ if (options.computerId != null) {
4121
+ params.set("computerId", options.computerId);
4122
+ }
4123
+ if (options.limit != null) {
4124
+ params.set("limit", String(options.limit));
4125
+ }
4126
+ if (options.cursor != null) {
4127
+ params.set("cursor", options.cursor);
4128
+ }
4129
+ const query = params.toString();
4130
+ const path3 = `/api/v0/sessions${query ? `?${query}` : ""}`;
4131
+ const body = await factoryFetch(path3, options.apiKey, options.baseUrl);
4132
+ return parseResponse(body, RemoteSessionListResponseSchema);
4133
+ }
4134
+
4135
+ // src/daemon/client.ts
4136
+ var DAEMON_SERVER_REQUEST_METHODS = {
4137
+ "daemon.request_permission": "permission" /* Permission */,
4138
+ "daemon.ask_user": "askUser" /* AskUser */
4139
+ };
4140
+ var DaemonClient = class {
4141
+ _engine;
4142
+ _apiKey;
4143
+ _sessionId = null;
4144
+ _closed = false;
4145
+ _notificationListeners = [];
4146
+ _permissionHandler = null;
4147
+ _askUserHandler = null;
4148
+ constructor(options) {
4149
+ this._apiKey = options.apiKey;
4150
+ this._engine = new ProtocolEngine({
4151
+ transport: options.transport,
4152
+ serverRequestMethodMap: DAEMON_SERVER_REQUEST_METHODS
4153
+ });
4154
+ this._engine.onNotification((notification) => {
4155
+ const normalized = notification["method"] === "daemon.session_notification" ? { ...notification, method: "droid.session_notification" } : notification;
4156
+ dispatchNotification(normalized, [...this._notificationListeners]);
4157
+ });
4158
+ this._engine.setPermissionHandler(
4159
+ (params) => this._dispatchPermissionRequest(params)
4160
+ );
4161
+ this._engine.setAskUserHandler(
4162
+ (params) => this._dispatchAskUserRequest(params)
4163
+ );
4164
+ }
4165
+ get sessionId() {
4166
+ return this._sessionId;
4167
+ }
4168
+ get isConnected() {
4169
+ return !this._closed && this._engine.isHealthy;
4170
+ }
4171
+ async initializeSession(params) {
4172
+ this._ensureNotClosed();
4173
+ const result = await this._rpc(
4174
+ "daemon.initialize_session" /* INITIALIZE_SESSION */,
4175
+ { ...params, token: this._apiKey },
4176
+ InitializeSessionResultSchema,
4177
+ SESSION_INIT_TIMEOUT
4178
+ );
4179
+ this._sessionId = result.sessionId;
4180
+ return result;
4181
+ }
4182
+ async loadSession(params) {
4183
+ this._ensureNotClosed();
4184
+ const result = await this._rpc(
4185
+ "daemon.load_session" /* LOAD_SESSION */,
4186
+ { ...params, token: this._apiKey },
4187
+ LoadSessionResultSchema,
4188
+ SESSION_INIT_TIMEOUT
4189
+ );
4190
+ this._sessionId = params.sessionId;
4191
+ return result;
4192
+ }
4193
+ async addUserMessage(params) {
4194
+ return this._sessionRpc(
4195
+ "daemon.add_user_message" /* ADD_USER_MESSAGE */,
4196
+ params,
4197
+ AddUserMessageResultSchema
4198
+ );
4199
+ }
4200
+ async interruptSession() {
4201
+ return this._sessionRpc(
4202
+ "daemon.interrupt_session" /* INTERRUPT_SESSION */,
4203
+ {},
4204
+ InterruptSessionResultSchema
4205
+ );
4206
+ }
4207
+ async closeSession(params = {}) {
4208
+ return this._sessionRpc(
4209
+ "daemon.close_session" /* CLOSE_SESSION */,
4210
+ params,
4211
+ CloseSessionResultSchema
4212
+ );
4213
+ }
4214
+ async updateSessionSettings(params) {
4215
+ return this._sessionRpc(
4216
+ "daemon.update_session_settings" /* UPDATE_SESSION_SETTINGS */,
4217
+ params,
4218
+ UpdateSessionSettingsResultSchema
4219
+ );
4220
+ }
4221
+ async compactSession(params = {}) {
4222
+ return this._sessionRpc(
4223
+ "daemon.compact_session" /* COMPACT_SESSION */,
4224
+ params,
4225
+ CompactSessionResultSchema,
4226
+ COMPACTION_TIMEOUT
4227
+ );
4228
+ }
4229
+ async forkSession() {
4230
+ return this._sessionRpc(
4231
+ "daemon.fork_session" /* FORK_SESSION */,
4232
+ {},
4233
+ ForkSessionResultSchema
4234
+ );
4235
+ }
4236
+ async getContextBreakdown() {
4237
+ return this._sessionRpc(
4238
+ "daemon.get_context_breakdown" /* GET_CONTEXT_STATS */,
4239
+ {},
4240
+ ContextBreakdownResultSchema
4241
+ );
4242
+ }
4243
+ async renameSession(params) {
4244
+ return this._sessionRpc(
4245
+ "daemon.rename_session" /* RENAME_SESSION */,
4246
+ params,
4247
+ RenameSessionResultSchema
4248
+ );
4249
+ }
4250
+ async getRewindInfo(params) {
4251
+ return this._sessionRpc(
4252
+ "daemon.get_rewind_info" /* GET_REWIND_INFO */,
4253
+ params,
4254
+ GetRewindInfoResultSchema,
4255
+ REWIND_TIMEOUT
4256
+ );
4257
+ }
4258
+ async executeRewind(params) {
4259
+ return this._sessionRpc(
4260
+ "daemon.execute_rewind" /* EXECUTE_REWIND */,
4261
+ params,
4262
+ ExecuteRewindResultSchema,
4263
+ REWIND_TIMEOUT
4264
+ );
4265
+ }
4266
+ async addMcpServer(params) {
4267
+ return this._sessionRpc(
4268
+ "daemon.add_mcp_server" /* ADD_MCP_SERVER */,
4269
+ params,
4270
+ AddMcpServerResultSchema
4271
+ );
4272
+ }
4273
+ async removeMcpServer(params) {
4274
+ return this._sessionRpc(
4275
+ "daemon.remove_mcp_server" /* REMOVE_MCP_SERVER */,
4276
+ params,
4277
+ RemoveMcpServerResultSchema
4278
+ );
4279
+ }
4280
+ async toggleMcpServer(params) {
4281
+ return this._sessionRpc(
4282
+ "daemon.toggle_mcp_server" /* TOGGLE_MCP_SERVER */,
4283
+ params,
4284
+ ToggleMcpServerResultSchema
4285
+ );
4286
+ }
4287
+ async listMcpServers() {
4288
+ return this._sessionRpc(
4289
+ "daemon.list_mcp_servers" /* LIST_MCP_SERVERS */,
4290
+ {},
4291
+ ListMcpServersResultSchema
4292
+ );
4293
+ }
4294
+ async listMcpTools() {
4295
+ return this._sessionRpc(
4296
+ "daemon.list_mcp_tools" /* LIST_MCP_TOOLS */,
4297
+ {},
4298
+ ListMcpToolsResultSchema
4299
+ );
4300
+ }
4301
+ async authenticateMcpServer(params) {
4302
+ return this._sessionRpc(
4303
+ "daemon.authenticate_mcp_server" /* AUTHENTICATE_MCP_SERVER */,
4304
+ params,
4305
+ AuthenticateMcpServerResultSchema
4306
+ );
4307
+ }
4308
+ async listSkills() {
4309
+ return this._sessionRpc(
4310
+ "daemon.list_skills" /* LIST_SKILLS */,
4311
+ {},
4312
+ ListSkillsResultSchema
4313
+ );
4314
+ }
4315
+ onNotification(callback, filter) {
4316
+ const entry = { callback, filter };
4317
+ this._notificationListeners.push(entry);
4318
+ let unsubscribed = false;
4319
+ return () => {
4320
+ if (!unsubscribed) {
4321
+ unsubscribed = true;
4322
+ const idx = this._notificationListeners.indexOf(entry);
4323
+ if (idx !== -1) {
4324
+ this._notificationListeners.splice(idx, 1);
4325
+ }
4326
+ }
4327
+ };
4328
+ }
4329
+ setPermissionHandler(handler) {
4330
+ this._permissionHandler = handler;
4331
+ }
4332
+ setAskUserHandler(handler) {
4333
+ this._askUserHandler = handler;
4334
+ }
4335
+ async close() {
4336
+ if (this._closed) {
4337
+ return;
4338
+ }
4339
+ this._closed = true;
4340
+ this._notificationListeners.length = 0;
4341
+ this._permissionHandler = null;
4342
+ this._askUserHandler = null;
4343
+ await this._engine.close();
4344
+ }
4345
+ async _rpc(method, params, schema, timeout) {
4346
+ const raw = await this._engine.sendRequest(method, params, timeout);
4347
+ return schema.parse(raw);
4348
+ }
4349
+ async _sessionRpc(method, params, schema, timeout) {
4350
+ this._ensureNotClosed();
4351
+ this._ensureSession();
4352
+ return this._rpc(
4353
+ method,
4354
+ { sessionId: this._sessionId, ...params },
4355
+ schema,
4356
+ timeout
4357
+ );
4358
+ }
4359
+ _dispatchPermissionRequest(params) {
4360
+ const handler = this._permissionHandler;
4361
+ if (handler == null) {
4362
+ return "cancel" /* Cancel */;
4363
+ }
4364
+ return handler(params);
4365
+ }
4366
+ _dispatchAskUserRequest(params) {
4367
+ const handler = this._askUserHandler;
4368
+ if (handler == null) {
4369
+ return { cancelled: true, answers: [] };
4370
+ }
4371
+ return handler(params);
4372
+ }
4373
+ _ensureNotClosed() {
4374
+ if (this._closed) {
4375
+ throw new ConnectionError(
4376
+ "Daemon client has been closed. Create a new DaemonClient instance."
4377
+ );
4378
+ }
4379
+ }
4380
+ _ensureSession() {
4381
+ if (this._sessionId == null) {
4382
+ throw new SessionError(
4383
+ "No active session. Call initializeSession or loadSession first."
4384
+ );
4385
+ }
4386
+ }
4387
+ };
4388
+
4389
+ // src/daemon/local.ts
4390
+ import { spawn as spawn2 } from "child_process";
4391
+ import * as fs2 from "fs";
4392
+ import * as net from "net";
4393
+ import * as os2 from "os";
4394
+ import * as path2 from "path";
4395
+ var SOCKET_TIMEOUT_MS = 2e3;
4396
+ var STARTUP_POLL_INTERVAL_MS = 250;
4397
+ var STARTUP_TIMEOUT_MS = 3e4;
4398
+ var MAX_STARTUP_ATTEMPTS = 3;
4399
+ var FACTORY_DIR = ".factory";
4400
+ var DEFAULT_PORT = 37643;
4401
+ var DAEMON_PORT_FILE = "daemon.port";
4402
+ var DEFAULT_HOST = "127.0.0.1";
4403
+ var spawnedDaemonProcess = null;
4404
+ var daemonTarget = null;
4405
+ var daemonEnsureInflight = null;
4406
+ function getFactoryHome() {
4407
+ return process.env.FACTORY_HOME_OVERRIDE || os2.homedir();
4408
+ }
4409
+ function getFactoryDirName() {
4410
+ return FACTORY_DIR;
4411
+ }
4412
+ function getFactoryDir() {
4413
+ return path2.join(getFactoryHome(), getFactoryDirName());
4414
+ }
4415
+ function getSdkDir() {
4416
+ return path2.join(getFactoryDir(), "sdk");
4417
+ }
4418
+ function getDefaultDaemonPort() {
4419
+ return DEFAULT_PORT;
4420
+ }
4421
+ function resolveExecPath() {
4422
+ const override = process.env.FACTORY_DROID_BINARY?.trim();
4423
+ if (override && override.length > 0) {
4424
+ if (override.includes("/") || override.includes("\\")) {
4425
+ try {
4426
+ fs2.accessSync(override, fs2.constants.X_OK);
4427
+ return override;
4428
+ } catch {
4429
+ }
4430
+ } else {
4431
+ return override;
4432
+ }
4433
+ }
4434
+ return "droid";
4435
+ }
4436
+ async function allocatePort() {
4437
+ return new Promise((resolve2, reject) => {
4438
+ const server = net.createServer();
4439
+ server.once("error", reject);
4440
+ server.listen(0, DEFAULT_HOST, () => {
4441
+ const address = server.address();
4442
+ if (!address || typeof address === "string") {
4443
+ server.close();
4444
+ reject(new Error("Failed to determine dynamic port"));
4445
+ return;
4446
+ }
4447
+ const port = address.port;
4448
+ server.close((err) => {
4449
+ if (err) reject(err);
4450
+ else resolve2(port);
4451
+ });
4452
+ });
4453
+ });
4454
+ }
4455
+ async function isDaemonReachable(port) {
4456
+ return new Promise((resolve2) => {
4457
+ const socket = net.createConnection({ host: DEFAULT_HOST, port });
4458
+ const cleanup = () => {
4459
+ socket.removeAllListeners();
4460
+ socket.destroy();
4461
+ };
4462
+ const fail = () => {
4463
+ cleanup();
4464
+ resolve2(false);
4465
+ };
4466
+ socket.setTimeout(SOCKET_TIMEOUT_MS, fail);
4467
+ socket.once("error", fail);
4468
+ socket.once("connect", () => {
4469
+ cleanup();
4470
+ resolve2(true);
4471
+ });
4472
+ });
4473
+ }
4474
+ async function waitForDaemonReady(child, port, timeoutMs = STARTUP_TIMEOUT_MS) {
4475
+ return new Promise((resolve2) => {
4476
+ let settled = false;
4477
+ let exited = false;
4478
+ const deadline = Date.now() + timeoutMs;
4479
+ const settle = (result) => {
4480
+ if (settled) return;
4481
+ settled = true;
4482
+ child.removeListener("exit", onExit);
4483
+ child.removeListener("error", onExit);
4484
+ resolve2(result);
4485
+ };
4486
+ const onExit = () => {
4487
+ exited = true;
4488
+ settle("exited");
4489
+ };
4490
+ child.once("exit", onExit);
4491
+ child.once("error", onExit);
4492
+ const poll = async () => {
4493
+ while (!exited && !settled) {
4494
+ if (await isDaemonReachable(port)) {
4495
+ settle("ready");
4496
+ return;
4497
+ }
4498
+ if (Date.now() >= deadline) {
4499
+ settle("timeout");
4500
+ return;
4501
+ }
4502
+ await new Promise((r) => setTimeout(r, STARTUP_POLL_INTERVAL_MS));
4503
+ }
4504
+ };
4505
+ void poll();
4506
+ });
4507
+ }
4508
+ function readPortFile() {
4509
+ try {
4510
+ const portPath = path2.join(getSdkDir(), DAEMON_PORT_FILE);
4511
+ const content = fs2.readFileSync(portPath, "utf-8").trim();
4512
+ const port = parseInt(content, 10);
4513
+ if (Number.isFinite(port) && port > 0 && port < 65536) {
4514
+ return port;
4515
+ }
4516
+ return null;
4517
+ } catch {
4518
+ return null;
4519
+ }
4520
+ }
4521
+ function writePortFile(port) {
4522
+ try {
4523
+ const sdkDir = getSdkDir();
4524
+ fs2.mkdirSync(sdkDir, { recursive: true });
4525
+ fs2.writeFileSync(path2.join(sdkDir, DAEMON_PORT_FILE), String(port), {
4526
+ mode: 384
4527
+ });
4528
+ } catch {
4529
+ }
4530
+ }
4531
+ function cacheTarget(port) {
4532
+ daemonTarget = { host: DEFAULT_HOST, port };
4533
+ return { port };
4534
+ }
4535
+ function clearSpawnedDaemonProcess(child) {
4536
+ if (spawnedDaemonProcess === child) {
4537
+ spawnedDaemonProcess = null;
4538
+ }
4539
+ }
4540
+ async function spawnDaemon(execPath, port) {
4541
+ const args = ["daemon", "--host", DEFAULT_HOST, "--port", String(port)];
4542
+ let stderrFd;
4543
+ try {
4544
+ const logsDir = path2.join(getSdkDir(), "logs");
4545
+ fs2.mkdirSync(logsDir, { recursive: true });
4546
+ stderrFd = fs2.openSync(path2.join(logsDir, "daemon-stderr.log"), "a");
4547
+ } catch {
4548
+ }
4549
+ const child = spawn2(execPath, args, {
4550
+ detached: true,
4551
+ stdio: ["ignore", "ignore", stderrFd ?? "ignore"],
4552
+ cwd: os2.homedir(),
4553
+ env: { ...process.env }
4554
+ });
4555
+ if (stderrFd !== void 0) {
4556
+ try {
4557
+ fs2.closeSync(stderrFd);
4558
+ } catch {
4559
+ }
4560
+ }
4561
+ spawnedDaemonProcess = child;
4562
+ child.unref();
4563
+ child.once("exit", () => {
4564
+ clearSpawnedDaemonProcess(child);
4565
+ });
4566
+ child.on("error", () => {
4567
+ clearSpawnedDaemonProcess(child);
4568
+ });
4569
+ const result = await waitForDaemonReady(child, port);
4570
+ if (result !== "ready") {
4571
+ clearSpawnedDaemonProcess(child);
4572
+ if (result === "timeout") {
4573
+ try {
4574
+ child.kill("SIGTERM");
4575
+ } catch {
4576
+ }
4577
+ }
4578
+ }
4579
+ return result;
4580
+ }
4581
+ async function _ensureLocalDaemon() {
4582
+ const execPath = resolveExecPath();
4583
+ const wellKnownPort = getDefaultDaemonPort();
4584
+ if (await isDaemonReachable(wellKnownPort)) {
4585
+ return cacheTarget(wellKnownPort);
4586
+ }
4587
+ const portFilePort = readPortFile();
4588
+ if (portFilePort !== null && portFilePort !== wellKnownPort) {
4589
+ if (await isDaemonReachable(portFilePort)) {
4590
+ return cacheTarget(portFilePort);
4591
+ }
4592
+ }
4593
+ const firstResult = await spawnDaemon(execPath, wellKnownPort);
4594
+ if (firstResult === "ready") {
4595
+ writePortFile(wellKnownPort);
4596
+ return cacheTarget(wellKnownPort);
4597
+ }
4598
+ for (let attempt = 2; attempt <= MAX_STARTUP_ATTEMPTS; attempt++) {
4599
+ const port = await allocatePort();
4600
+ const result = await spawnDaemon(execPath, port);
4601
+ if (result === "ready") {
4602
+ writePortFile(port);
4603
+ return cacheTarget(port);
4604
+ }
4605
+ }
4606
+ throw new ConnectionError(
4607
+ `Failed to start local droid daemon after ${MAX_STARTUP_ATTEMPTS} attempts. Ensure the \`droid\` CLI is installed and \`droid auth login\` has been run.`
4608
+ );
4609
+ }
4610
+ async function ensureLocalDaemon() {
4611
+ if (daemonTarget) {
4612
+ if (await isDaemonReachable(daemonTarget.port)) {
4613
+ return { port: daemonTarget.port };
4614
+ }
4615
+ daemonTarget = null;
4616
+ }
4617
+ if (!daemonEnsureInflight) {
4618
+ daemonEnsureInflight = _ensureLocalDaemon().finally(() => {
4619
+ daemonEnsureInflight = null;
4620
+ });
4621
+ }
4622
+ return daemonEnsureInflight;
4623
+ }
4624
+
4625
+ // src/daemon/session.ts
4626
+ var DaemonSession = class {
4627
+ _client;
4628
+ _sessionId;
4629
+ _closed = false;
4630
+ _activeBridges = /* @__PURE__ */ new Set();
4631
+ _cleanupCallbacks = [];
4632
+ /** @internal */
4633
+ constructor(client, sessionId) {
4634
+ this._client = client;
4635
+ this._sessionId = sessionId;
4636
+ }
4637
+ /** @internal */
4638
+ addCleanup(cleanup) {
4639
+ this._cleanupCallbacks.push(cleanup);
4640
+ }
4641
+ get sessionId() {
4642
+ return this._sessionId;
4643
+ }
4644
+ async *stream(prompt, options) {
4645
+ this._ensureNotClosed();
4646
+ yield* streamFromClient(
4647
+ this._client,
4648
+ this._sessionId,
4649
+ this._activeBridges,
4650
+ prompt,
4651
+ options
4652
+ );
4653
+ }
4654
+ async send(prompt, options) {
4655
+ this._ensureNotClosed();
4656
+ await this._client.addUserMessage({
4657
+ text: prompt,
4658
+ images: options?.images,
4659
+ files: options?.files,
4660
+ outputFormat: options?.outputFormat
4661
+ });
4662
+ }
4663
+ async interrupt() {
4664
+ this._ensureNotClosed();
4665
+ await this._client.interruptSession();
4666
+ }
4667
+ async close() {
4668
+ if (this._closed) {
4669
+ return;
4670
+ }
4671
+ this._closed = true;
4672
+ for (const bridge of this._activeBridges) {
4673
+ bridge.signalDone();
4674
+ }
4675
+ try {
4676
+ await this._client.closeSession({ reason: "other" }).catch(() => {
4677
+ });
4678
+ } finally {
4679
+ await this._client.close();
4680
+ for (const cleanup of this._cleanupCallbacks) {
4681
+ try {
4682
+ await cleanup();
4683
+ } catch {
4684
+ }
4685
+ }
4686
+ this._cleanupCallbacks.length = 0;
4687
+ }
4688
+ }
4689
+ async updateSettings(params) {
4690
+ this._ensureNotClosed();
4691
+ return this._client.updateSessionSettings(params);
4692
+ }
4693
+ async enterSpecMode(params = {}) {
4694
+ this._ensureNotClosed();
4695
+ return this._client.updateSessionSettings({
4696
+ interactionMode: "spec" /* Spec */,
4697
+ ...params
4698
+ });
4699
+ }
4700
+ async compactSession(params) {
4701
+ this._ensureNotClosed();
4702
+ return this._client.compactSession(params ?? {});
4703
+ }
4704
+ async forkSession() {
4705
+ this._ensureNotClosed();
4706
+ return this._client.forkSession();
4707
+ }
4708
+ async getContextBreakdown() {
4709
+ this._ensureNotClosed();
4710
+ return this._client.getContextBreakdown();
4711
+ }
4712
+ async renameSession(params) {
4713
+ this._ensureNotClosed();
4714
+ return this._client.renameSession(params);
4715
+ }
4716
+ async getRewindInfo(params) {
4717
+ this._ensureNotClosed();
4718
+ return this._client.getRewindInfo(params);
4719
+ }
4720
+ async executeRewind(params) {
4721
+ this._ensureNotClosed();
4722
+ return this._client.executeRewind(params);
4723
+ }
4724
+ async addMcpServer(params) {
4725
+ this._ensureNotClosed();
4726
+ return this._client.addMcpServer(params);
4727
+ }
4728
+ async removeMcpServer(params) {
4729
+ this._ensureNotClosed();
4730
+ return this._client.removeMcpServer(params);
4731
+ }
4732
+ async toggleMcpServer(params) {
4733
+ this._ensureNotClosed();
4734
+ return this._client.toggleMcpServer(params);
4735
+ }
4736
+ async listMcpServers() {
4737
+ this._ensureNotClosed();
4738
+ return this._client.listMcpServers();
4739
+ }
4740
+ async listMcpTools() {
4741
+ this._ensureNotClosed();
4742
+ return this._client.listMcpTools();
4743
+ }
4744
+ async authenticateMcpServer(params) {
4745
+ this._ensureNotClosed();
4746
+ return this._client.authenticateMcpServer(params);
4747
+ }
4748
+ async listSkills() {
4749
+ this._ensureNotClosed();
4750
+ return this._client.listSkills();
4751
+ }
4752
+ onNotification(callback, filter) {
4753
+ return this._client.onNotification(callback, filter);
4754
+ }
4755
+ _ensureNotClosed() {
4756
+ if (this._closed) {
4757
+ throw new ConnectionError(
4758
+ "Daemon session has been closed. Create a new session to continue."
4759
+ );
4760
+ }
4761
+ }
4762
+ };
4763
+
4764
+ // src/daemon/transport.ts
4765
+ import WebSocket from "ws";
4766
+
4767
+ // src/daemon/types.ts
4768
+ var MachineType = /* @__PURE__ */ ((MachineType2) => {
4769
+ MachineType2["Local"] = "local";
4770
+ MachineType2["Computer"] = "computer";
4771
+ return MachineType2;
4772
+ })(MachineType || {});
4773
+ var DEFAULT_DAEMON_PORT = 37643;
4774
+ var DEFAULT_RELAY_BASE_URL = "wss://relay.factory.ai";
4775
+ var DEFAULT_WS_CONFIG = {
4776
+ maxConnectRetries: 5,
4777
+ initialRetryDelayMs: 500,
4778
+ maxRetryDelayMs: 5e3,
4779
+ connectionTimeoutMs: 5e3
4780
+ };
4781
+ var COMPUTER_WS_CONFIG = {
4782
+ connectionTimeoutMs: 45e3,
4783
+ maxConnectRetries: 10,
4784
+ initialRetryDelayMs: 2e3,
4785
+ maxRetryDelayMs: 1e4
4786
+ };
4787
+
4788
+ // src/daemon/transport.ts
4789
+ var WebSocketTransport = class {
4790
+ maxConnectRetries;
4791
+ initialRetryDelayMs;
4792
+ maxRetryDelayMs;
4793
+ connectionTimeoutMs;
4794
+ ws = null;
4795
+ messageHandler = null;
4796
+ errorHandler = null;
4797
+ _isConnected = false;
4798
+ isClosing = false;
4799
+ constructor(options = {}) {
4800
+ this.maxConnectRetries = options.maxConnectRetries ?? DEFAULT_WS_CONFIG.maxConnectRetries;
4801
+ this.initialRetryDelayMs = options.initialRetryDelayMs ?? DEFAULT_WS_CONFIG.initialRetryDelayMs;
4802
+ this.maxRetryDelayMs = options.maxRetryDelayMs ?? DEFAULT_WS_CONFIG.maxRetryDelayMs;
4803
+ this.connectionTimeoutMs = options.connectionTimeoutMs ?? DEFAULT_WS_CONFIG.connectionTimeoutMs;
4804
+ }
4805
+ get isConnected() {
4806
+ return this._isConnected;
4807
+ }
4808
+ async connect(url) {
4809
+ if (!url) {
4810
+ throw new ConnectionError("WebSocket URL is required");
4811
+ }
4812
+ if (this._isConnected) {
4813
+ throw new ConnectionError("Transport already connected");
4814
+ }
4815
+ this.isClosing = false;
4816
+ let lastError;
4817
+ for (let attempt = 0; attempt <= this.maxConnectRetries; attempt++) {
4818
+ try {
4819
+ await this._doConnect(url);
4820
+ return;
4821
+ } catch (error) {
4822
+ lastError = error instanceof Error ? error : new Error(String(error));
4823
+ if (attempt < this.maxConnectRetries) {
4824
+ const delay = Math.min(
4825
+ this.initialRetryDelayMs * 2 ** attempt,
4826
+ this.maxRetryDelayMs
4827
+ );
4828
+ await new Promise((resolve2) => setTimeout(resolve2, delay));
4829
+ }
4830
+ }
4831
+ }
4832
+ throw lastError;
4833
+ }
4834
+ send(message) {
4835
+ if (!this._isConnected || !this.ws) {
4836
+ throw new ConnectionError("WebSocket transport not connected");
4837
+ }
4838
+ try {
4839
+ this.ws.send(JSON.stringify(message));
4840
+ } catch (error) {
4841
+ const msg = error instanceof Error ? error.message : String(error);
4842
+ throw new ConnectionError(`Failed to send WebSocket message: ${msg}`);
4843
+ }
4844
+ }
4845
+ onMessage(callback) {
4846
+ this.messageHandler = callback;
4847
+ }
4848
+ onError(callback) {
4849
+ this.errorHandler = callback;
4850
+ }
4851
+ async close() {
4852
+ if (this.isClosing) {
4853
+ return;
4854
+ }
4855
+ this.isClosing = true;
4856
+ this._isConnected = false;
4857
+ const ws = this.ws;
4858
+ this.ws = null;
4859
+ if (ws && ws.readyState === WebSocket.OPEN) {
4860
+ await new Promise((resolve2) => {
4861
+ ws.once("close", () => resolve2());
4862
+ ws.close(1e3, "Client disconnect");
4863
+ setTimeout(() => resolve2(), 5e3);
4864
+ });
4865
+ }
4866
+ this.isClosing = false;
4867
+ }
4868
+ async _doConnect(url) {
4869
+ return new Promise((resolve2, reject) => {
4870
+ let settled = false;
4871
+ let timeoutId = null;
4872
+ const cleanup = () => {
4873
+ if (timeoutId) {
4874
+ clearTimeout(timeoutId);
4875
+ timeoutId = null;
4876
+ }
4877
+ };
4878
+ const ws = new WebSocket(url);
4879
+ const handleOpen = () => {
4880
+ if (settled) return;
4881
+ settled = true;
4882
+ cleanup();
4883
+ this.ws = ws;
4884
+ this._isConnected = true;
4885
+ this._setupOngoingHandlers(ws);
4886
+ resolve2();
4887
+ };
4888
+ const handleError = (error) => {
4889
+ if (settled) return;
4890
+ settled = true;
4891
+ cleanup();
4892
+ try {
4893
+ ws.close();
4894
+ } catch {
4895
+ }
4896
+ reject(
4897
+ new ConnectionError(`WebSocket connection failed: ${error.message}`)
4898
+ );
4899
+ };
4900
+ const handleClose = (code, reason) => {
4901
+ if (settled) return;
4902
+ settled = true;
4903
+ cleanup();
4904
+ reject(
4905
+ new ConnectionError(
4906
+ `WebSocket closed before opening (code: ${code}, reason: ${reason.toString()})`
4907
+ )
4908
+ );
4909
+ };
4910
+ ws.once("open", handleOpen);
4911
+ ws.once("error", handleError);
4912
+ ws.once("close", handleClose);
4913
+ timeoutId = setTimeout(() => {
4914
+ if (settled) return;
4915
+ settled = true;
4916
+ try {
4917
+ ws.close();
4918
+ } catch {
4919
+ }
4920
+ reject(
4921
+ new ConnectionError(
4922
+ `WebSocket connection timeout after ${this.connectionTimeoutMs}ms: ${url}`
4923
+ )
4924
+ );
4925
+ }, this.connectionTimeoutMs);
4926
+ });
4927
+ }
4928
+ _setupOngoingHandlers(ws) {
4929
+ ws.on("message", (data) => {
4930
+ if (!this.messageHandler) return;
4931
+ const text = typeof data === "string" ? data : data.toString();
4932
+ try {
4933
+ const parsed = JSON.parse(text);
4934
+ if (isRecord(parsed)) {
4935
+ this.messageHandler(parsed);
4936
+ }
4937
+ } catch {
4938
+ }
4939
+ });
4940
+ ws.on("error", (error) => {
4941
+ if (!this.isClosing && this.errorHandler) {
4942
+ this.errorHandler(
4943
+ new ConnectionError(`WebSocket error: ${error.message}`)
4944
+ );
4945
+ }
4946
+ });
4947
+ ws.on("close", (code, reason) => {
4948
+ this._isConnected = false;
4949
+ this.ws = null;
4950
+ if (!this.isClosing && this.errorHandler) {
4951
+ this.errorHandler(
4952
+ new ConnectionError(
4953
+ `WebSocket closed (code: ${code}, reason: ${reason.toString()})`
4954
+ )
4955
+ );
4956
+ }
4957
+ });
4958
+ }
4959
+ };
4960
+
4961
+ // src/daemon/connection.ts
4962
+ var DAEMON_AUTHENTICATE_METHOD = "daemon.authenticate";
4963
+ var DAEMON_AUTHENTICATE_TIMEOUT = 3e4;
4964
+ var SDK_CALLER = "droid-sdk";
4965
+ function resolveWebSocketUrl(options) {
4966
+ if (options.url) {
4967
+ return options.url;
4968
+ }
4969
+ const machine = options.machine;
4970
+ if (!machine || machine.type === "local" /* Local */) {
4971
+ const port = options._localPort ?? options.daemonPort ?? DEFAULT_DAEMON_PORT;
4972
+ return `ws://127.0.0.1:${port}`;
4973
+ }
4974
+ if (machine.type === "computer" /* Computer */) {
4975
+ const relayBase = options.relayBaseUrl ?? DEFAULT_RELAY_BASE_URL;
4976
+ return `${relayBase}/v0/computer/${machine.computerId}/client`;
4977
+ }
4978
+ const _exhaustive = machine;
4979
+ throw new ConnectionError(`Unsupported machine type: ${String(_exhaustive)}`);
4980
+ }
4981
+ function getWebSocketConfig(machine) {
4982
+ if (machine?.type === "computer" /* Computer */) {
4983
+ return { ...DEFAULT_WS_CONFIG, ...COMPUTER_WS_CONFIG };
4984
+ }
4985
+ if (!machine || machine.type === "local" /* Local */) {
4986
+ return { ...DEFAULT_WS_CONFIG, connectionTimeoutMs: 1e4 };
4987
+ }
4988
+ return { ...DEFAULT_WS_CONFIG };
4989
+ }
4990
+ var SharedTransportMultiplexer = class {
4991
+ constructor(_inner) {
4992
+ this._inner = _inner;
4993
+ this._inner.onMessage((message) => {
4994
+ this._routeMessage(message);
4995
+ });
4996
+ this._inner.onError((error) => {
4997
+ for (const view of this._views) {
4998
+ if (view.errorCallback) {
4999
+ try {
5000
+ view.errorCallback(error);
5001
+ } catch {
5002
+ }
5003
+ }
5004
+ }
5005
+ });
5006
+ }
5007
+ _views = /* @__PURE__ */ new Set();
5008
+ createView() {
5009
+ const inner = this._inner;
5010
+ const viewState = {
5011
+ messageCallback: null,
5012
+ errorCallback: null,
5013
+ pendingRequestIds: /* @__PURE__ */ new Set(),
5014
+ sessionId: null
5015
+ };
5016
+ this._views.add(viewState);
5017
+ const view = {
5018
+ send: (message) => {
5019
+ if (message["type"] === "request" && typeof message["id"] === "string") {
5020
+ viewState.pendingRequestIds.add(message["id"]);
5021
+ }
5022
+ inner.send(message);
5023
+ },
5024
+ onMessage: (callback) => {
5025
+ viewState.messageCallback = callback;
5026
+ },
5027
+ onError: (callback) => {
5028
+ viewState.errorCallback = callback;
5029
+ },
5030
+ close: async () => {
5031
+ viewState.messageCallback = null;
5032
+ viewState.errorCallback = null;
5033
+ viewState.pendingRequestIds.clear();
5034
+ this._views.delete(viewState);
5035
+ },
5036
+ get isConnected() {
5037
+ return inner.isConnected;
5038
+ }
5039
+ };
5040
+ return view;
5041
+ }
5042
+ _routeMessage(message) {
5043
+ const messageType = message["type"];
5044
+ const messageId = message["id"];
5045
+ if (messageType === "response" && typeof messageId === "string") {
5046
+ for (const view of this._views) {
5047
+ if (view.pendingRequestIds.has(messageId)) {
5048
+ view.pendingRequestIds.delete(messageId);
5049
+ const result = message["result"];
5050
+ if (isRecord(result) && typeof result["sessionId"] === "string") {
5051
+ view.sessionId = result["sessionId"];
5052
+ }
5053
+ if (view.messageCallback) {
5054
+ try {
5055
+ view.messageCallback(message);
5056
+ } catch {
5057
+ }
5058
+ }
5059
+ return;
5060
+ }
5061
+ }
5062
+ return;
5063
+ }
5064
+ if (messageType === "notification") {
5065
+ const params = message["params"];
5066
+ const sessionId = isRecord(params) && typeof params["sessionId"] === "string" ? params["sessionId"] : null;
5067
+ if (sessionId) {
5068
+ for (const view of this._views) {
5069
+ if (view.sessionId === sessionId && view.messageCallback) {
5070
+ try {
5071
+ view.messageCallback(message);
5072
+ } catch {
5073
+ }
5074
+ return;
5075
+ }
5076
+ }
5077
+ }
5078
+ for (const view of this._views) {
5079
+ if (view.messageCallback) {
5080
+ try {
5081
+ view.messageCallback(message);
5082
+ } catch {
5083
+ }
5084
+ }
5085
+ }
5086
+ return;
5087
+ }
5088
+ if (messageType === "request") {
5089
+ const params = message["params"];
5090
+ const sessionId = isRecord(params) && typeof params["sessionId"] === "string" ? params["sessionId"] : null;
5091
+ if (sessionId) {
5092
+ for (const view of this._views) {
5093
+ if (view.sessionId === sessionId && view.messageCallback) {
5094
+ try {
5095
+ view.messageCallback(message);
5096
+ } catch {
5097
+ }
5098
+ return;
5099
+ }
5100
+ }
5101
+ }
5102
+ for (const view of this._views) {
5103
+ if (view.messageCallback) {
5104
+ try {
5105
+ view.messageCallback(message);
5106
+ } catch {
5107
+ }
5108
+ }
5109
+ }
5110
+ }
5111
+ }
5112
+ };
5113
+ async function authenticate(transport, options) {
5114
+ const requestId = crypto.randomUUID();
5115
+ const envelope = {
5116
+ jsonrpc: JSONRPC_VERSION,
5117
+ factoryApiVersion: LEGACY_FACTORY_API_VERSION,
5118
+ factoryProtocolVersion: FACTORY_PROTOCOL_VERSION,
5119
+ type: "request" /* Request */,
5120
+ id: requestId,
5121
+ method: DAEMON_AUTHENTICATE_METHOD,
5122
+ params: {
5123
+ apiKey: options.apiKey,
5124
+ caller: SDK_CALLER
5125
+ }
5126
+ };
5127
+ return new Promise((resolve2, reject) => {
5128
+ const timer = setTimeout(() => {
5129
+ transport.onMessage(() => {
5130
+ });
5131
+ reject(
5132
+ new ConnectionError(
5133
+ `Daemon authentication timed out after ${DAEMON_AUTHENTICATE_TIMEOUT}ms`
5134
+ )
5135
+ );
5136
+ }, DAEMON_AUTHENTICATE_TIMEOUT);
5137
+ transport.onMessage((message) => {
5138
+ if (message["id"] !== requestId) {
5139
+ return;
5140
+ }
5141
+ clearTimeout(timer);
5142
+ transport.onMessage(() => {
5143
+ });
5144
+ if (message["error"]) {
5145
+ const error = message["error"];
5146
+ const errorMessage = error && typeof error === "object" && "message" in error ? String(error.message) : "unknown error";
5147
+ reject(
5148
+ new ConnectionError(`Daemon authentication failed: ${errorMessage}`)
5149
+ );
5150
+ return;
5151
+ }
5152
+ resolve2();
5153
+ });
5154
+ transport.send(envelope);
5155
+ });
5156
+ }
5157
+ var DaemonConnection = class {
5158
+ _transport;
5159
+ _multiplexer;
5160
+ _apiKey;
5161
+ _closed = false;
5162
+ /** @internal */
5163
+ constructor(transport, apiKey) {
5164
+ this._transport = transport;
5165
+ this._multiplexer = new SharedTransportMultiplexer(transport);
5166
+ this._apiKey = apiKey;
5167
+ }
5168
+ async createSession(options = {}) {
5169
+ this._ensureNotClosed();
5170
+ const view = this._multiplexer.createView();
5171
+ const client = new DaemonClient({
5172
+ transport: view,
5173
+ apiKey: this._apiKey
5174
+ });
5175
+ if (options.permissionHandler) {
5176
+ client.setPermissionHandler(options.permissionHandler);
5177
+ }
5178
+ if (options.askUserHandler) {
5179
+ client.setAskUserHandler(options.askUserHandler);
5180
+ }
5181
+ let sdkMcpServers;
5182
+ try {
5183
+ sdkMcpServers = await startSdkMcpServers(options.mcpServers);
5184
+ const initParams = buildInitParams({
5185
+ ...options,
5186
+ mcpServers: sdkMcpServers.mcpServers
5187
+ });
5188
+ const initResult = await client.initializeSession(initParams);
5189
+ const session = new DaemonSession(client, initResult.sessionId);
5190
+ if (sdkMcpServers) {
5191
+ session.addCleanup(sdkMcpServers.cleanup);
5192
+ }
5193
+ return session;
5194
+ } catch (error) {
5195
+ await sdkMcpServers?.cleanup();
5196
+ await client.close();
5197
+ throw error;
5198
+ }
5199
+ }
5200
+ async resumeSession(sessionId, options = {}) {
5201
+ this._ensureNotClosed();
5202
+ const view = this._multiplexer.createView();
5203
+ const client = new DaemonClient({
5204
+ transport: view,
5205
+ apiKey: this._apiKey
5206
+ });
5207
+ if (options.permissionHandler) {
5208
+ client.setPermissionHandler(options.permissionHandler);
5209
+ }
5210
+ if (options.askUserHandler) {
5211
+ client.setAskUserHandler(options.askUserHandler);
5212
+ }
5213
+ let sdkMcpServers;
5214
+ try {
5215
+ sdkMcpServers = await startSdkMcpServers(options.mcpServers);
5216
+ await client.loadSession({
5217
+ sessionId,
5218
+ mcpServers: sdkMcpServers.mcpServers
5219
+ });
5220
+ const session = new DaemonSession(client, sessionId);
5221
+ if (sdkMcpServers) {
5222
+ session.addCleanup(sdkMcpServers.cleanup);
5223
+ }
5224
+ return session;
5225
+ } catch (error) {
5226
+ await sdkMcpServers?.cleanup();
5227
+ await client.close();
5228
+ throw error;
5229
+ }
5230
+ }
5231
+ async interruptSession(sessionId) {
5232
+ this._ensureNotClosed();
5233
+ const view = this._multiplexer.createView();
5234
+ const client = new DaemonClient({
5235
+ transport: view,
5236
+ apiKey: this._apiKey
5237
+ });
5238
+ try {
5239
+ await client.loadSession({ sessionId });
5240
+ await client.interruptSession();
5241
+ } finally {
5242
+ await client.close();
5243
+ }
5244
+ }
5245
+ async close() {
5246
+ if (this._closed) {
5247
+ return;
5248
+ }
5249
+ this._closed = true;
5250
+ await this._transport.close();
5251
+ }
5252
+ _ensureNotClosed() {
5253
+ if (this._closed) {
5254
+ throw new ConnectionError(
5255
+ "Daemon connection has been closed. Call connectDaemon() to create a new connection."
5256
+ );
5257
+ }
5258
+ }
5259
+ };
5260
+ async function connectDaemon(options) {
5261
+ const isLocal = !options.url && (!options.machine || options.machine.type === "local" /* Local */);
5262
+ let resolvedOptions = options;
5263
+ if (isLocal) {
5264
+ const { port } = await ensureLocalDaemon();
5265
+ resolvedOptions = { ...options, _localPort: port };
5266
+ }
5267
+ const url = resolveWebSocketUrl(resolvedOptions);
5268
+ const wsConfig = getWebSocketConfig(resolvedOptions.machine);
5269
+ const transport = new WebSocketTransport(wsConfig);
5270
+ const apiKey = resolvedOptions.apiKey;
5271
+ const maxRetries = resolvedOptions.maxRetries ?? 0;
5272
+ try {
5273
+ if (maxRetries > 0) {
5274
+ let lastError;
5275
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
5276
+ try {
5277
+ await transport.connect(url);
5278
+ await authenticate(transport, resolvedOptions);
5279
+ return new DaemonConnection(transport, apiKey);
5280
+ } catch (error) {
5281
+ lastError = error instanceof Error ? error : new Error(String(error));
5282
+ try {
5283
+ await transport.close();
5284
+ } catch {
5285
+ }
5286
+ if (attempt < maxRetries) {
5287
+ await new Promise((resolve2) => setTimeout(resolve2, 2e3));
5288
+ }
5289
+ }
5290
+ }
5291
+ throw lastError;
5292
+ }
5293
+ await transport.connect(url);
5294
+ await authenticate(transport, resolvedOptions);
5295
+ return new DaemonConnection(transport, apiKey);
5296
+ } catch (error) {
5297
+ try {
5298
+ await transport.close();
5299
+ } catch {
5300
+ }
5301
+ throw error;
5302
+ }
5303
+ }
3842
5304
  export {
3843
5305
  AddMcpServerRequestParamsSchema,
3844
5306
  AddMcpServerRequestSchema,
@@ -3889,13 +5351,22 @@ export {
3889
5351
  CompactSessionRequestSchema,
3890
5352
  CompactSessionResponseSchema,
3891
5353
  CompactSessionResultSchema,
5354
+ ComputerListResponseSchema,
5355
+ ComputerMetricSchema,
5356
+ ComputerMetricsResponseSchema,
5357
+ ComputerSchema,
3892
5358
  ConnectionError,
3893
5359
  ContentBlockSchema,
5360
+ ContextBreakdownCategorySchema,
5361
+ ContextBreakdownResultSchema,
3894
5362
  ContextStatsAccuracy,
3895
5363
  ContextStatsSchema,
3896
5364
  CreateMessageNotificationSchema,
3897
5365
  CreateToolConfirmationDetailsSchema,
3898
5366
  DEFAULT_REQUEST_TIMEOUT,
5367
+ DaemonClient,
5368
+ DaemonConnection,
5369
+ DaemonSession,
3899
5370
  DecompSessionType,
3900
5371
  DiscoveredIssueSchema,
3901
5372
  DismissalRecordSchema,
@@ -4010,6 +5481,10 @@ export {
4010
5481
  LoadSessionResponseSchema,
4011
5482
  LoadSessionResultSchema,
4012
5483
  MCP_AUTH_TIMEOUT,
5484
+ MachineTemplateBuildStatusSchema,
5485
+ MachineTemplateListResponseSchema,
5486
+ MachineTemplateSchema,
5487
+ MachineType,
4013
5488
  McpAuthCompletedNotificationSchema,
4014
5489
  McpAuthOutcome,
4015
5490
  McpAuthRequiredNotificationSchema,
@@ -4055,6 +5530,9 @@ export {
4055
5530
  REWIND_TIMEOUT,
4056
5531
  ReasoningEffort,
4057
5532
  RedactedThinkingBlockSchema,
5533
+ RefreshComputerResponseSchema,
5534
+ RemoteSessionListResponseSchema,
5535
+ RemoteSessionSchema,
4058
5536
  RemoveMcpServerRequestParamsSchema,
4059
5537
  RemoveMcpServerRequestSchema,
4060
5538
  RemoveMcpServerResponseSchema,
@@ -4074,6 +5552,7 @@ export {
4074
5552
  SDK_VERSION,
4075
5553
  SESSION_INIT_TIMEOUT,
4076
5554
  SdkMcpServer,
5555
+ ServerRequestHandlerType,
4077
5556
  SessionError,
4078
5557
  SessionMetadataSchema,
4079
5558
  SessionNotFoundError,
@@ -4149,20 +5628,37 @@ export {
4149
5628
  UpdateSessionSettingsResultSchema,
4150
5629
  VerificationCommandSchema,
4151
5630
  VerificationSchema,
5631
+ WebSocketTransport,
4152
5632
  WorkerCompletedEntrySchema,
4153
5633
  WorkerFailedEntrySchema,
4154
5634
  WorkerPausedEntrySchema,
4155
5635
  WorkerSelectedFeatureEntrySchema,
4156
5636
  WorkerStartedEntrySchema,
4157
5637
  WorkerStateInfoSchema,
5638
+ connectDaemon,
4158
5639
  convertNotificationToStreamMessage,
5640
+ createComputer,
4159
5641
  createResponseSchema,
4160
5642
  createSdkMcpServer,
4161
5643
  createSession,
5644
+ deleteComputer,
4162
5645
  dispatchNotification,
5646
+ ensureLocalDaemon,
5647
+ getComputer,
5648
+ getComputerByName,
5649
+ getComputerMetrics,
5650
+ getMachineTemplate,
5651
+ listComputers,
5652
+ listMachineTemplates,
5653
+ listRemoteSessions,
4163
5654
  listSessions,
5655
+ refreshComputer,
5656
+ resolveWebSocketUrl,
5657
+ restartComputer,
4164
5658
  resumeSession,
5659
+ retryInstallDeps,
4165
5660
  run,
4166
- tool
5661
+ tool,
5662
+ updateComputer
4167
5663
  };
4168
5664
  //# sourceMappingURL=index.js.map