@miosa/sdk 1.2.4 → 1.2.5

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
@@ -592,13 +592,6 @@ var Admin = class {
592
592
  model_id: modelId
593
593
  });
594
594
  }
595
- /** POST /api/v1/admin/impersonate — returns {token, expires_at}. */
596
- impersonate(externalUserId, options = {}) {
597
- return this.http.post("/admin/impersonate", {
598
- external_user_id: externalUserId,
599
- ttl_sec: options.ttlSec ?? 3600
600
- });
601
- }
602
595
  };
603
596
 
604
597
  // src/resources/analytics.ts
@@ -681,17 +674,6 @@ var ApiKeys = class {
681
674
  });
682
675
  return unwrap2(data);
683
676
  }
684
- /** POST /api/v1/api-keys/scoped — L2 delegation token bound to one external user. */
685
- async createScoped(params) {
686
- const body = stripUndefined2({
687
- external_user_id: params.externalUserId,
688
- scopes: params.scopes,
689
- expires_at: params.expiresAt
690
- });
691
- return unwrap2(
692
- await this.http.post("/api-keys/scoped", body)
693
- );
694
- }
695
677
  async delete(keyId) {
696
678
  await this.http.delete(`/api-keys/${keyId}`);
697
679
  }
@@ -3639,47 +3621,41 @@ function stripUndefined10(input) {
3639
3621
  Object.entries(input).filter(([, v]) => v !== void 0)
3640
3622
  );
3641
3623
  }
3642
- function dockerDeployMetadata(metadata) {
3643
- return {
3624
+ function dockerDeployMetadata(metadata, templateId) {
3625
+ return stripUndefined10({
3644
3626
  ...metadata ?? {},
3645
- deployment_product: "docker_deploy"
3646
- };
3627
+ deployment_product: "docker_deploy",
3628
+ docker_deploy_template_id: templateId
3629
+ });
3647
3630
  }
3648
- function dockerDeployProduct(deployment) {
3649
- return deployment.deployment_product ?? deployment.metadata?.["deployment_product"];
3631
+ function isRecord(value) {
3632
+ return !!value && typeof value === "object" && !Array.isArray(value);
3650
3633
  }
3651
- function dockerDeployHostId(deployment) {
3652
- const metadataHost = deployment.metadata?.["docker_deploy_host_id"];
3653
- return deployment.docker_deploy_host_id ?? (typeof metadataHost === "string" ? metadataHost : null);
3634
+ function stringValue(value) {
3635
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3654
3636
  }
3655
- function dockerDeployApp(deployment) {
3656
- const app = deployment.metadata?.["docker_deploy"];
3657
- if (!app || typeof app !== "object" || Array.isArray(app)) return null;
3658
- return app;
3637
+ function runtimeMetadata(metadata) {
3638
+ const runtime = metadata.runtime;
3639
+ return isRecord(runtime) ? runtime : void 0;
3640
+ }
3641
+ function publicProbeUrl(publicUrl, probePath) {
3642
+ const url = new URL(publicUrl);
3643
+ const normalizedPath = probePath.startsWith("/") ? probePath : `/${probePath}`;
3644
+ url.pathname = normalizedPath;
3645
+ return url.toString();
3659
3646
  }
3660
- function dockerDeployAppPort(app) {
3661
- const url = app?.["url"];
3662
- if (typeof url !== "string") return null;
3647
+ function looksLikeMiosaGatewayJson(body) {
3663
3648
  try {
3664
- const parsed = new URL(url);
3665
- const port = Number.parseInt(parsed.port, 10);
3666
- return Number.isInteger(port) ? port : null;
3649
+ const parsed = JSON.parse(body);
3650
+ return isRecord(parsed) && parsed.ok === true && typeof parsed.run_id === "string" && Object.keys(parsed).every((key) => ["ok", "run_id"].includes(key));
3667
3651
  } catch {
3668
- return null;
3652
+ return false;
3669
3653
  }
3670
3654
  }
3671
- function addDoctorCheck(checks, name, ok, message, details) {
3672
- checks.push({ name, ok, message, ...details ? { details } : {} });
3673
- }
3674
- function hostHealthy(host) {
3675
- return Boolean(
3676
- host && host.status === "active" && host.appliance_status === "healthy"
3677
- );
3678
- }
3679
- function probeUrl(publicUrl, probePath) {
3680
- const url = new URL(publicUrl);
3681
- url.pathname = probePath.startsWith("/") ? probePath : `/${probePath}`;
3682
- return url.toString();
3655
+ function addDoctorCheck(checks, name, ok, message, data) {
3656
+ const check = { name, ok, message };
3657
+ if (data !== void 0) check.data = data;
3658
+ checks.push(check);
3683
3659
  }
3684
3660
  var DeploymentVersions = class {
3685
3661
  constructor(http, deploymentId) {
@@ -3853,6 +3829,7 @@ var Deployments = class {
3853
3829
  run_command: params.runCommand ?? params.run_command,
3854
3830
  auto_deploy: params.autoDeploy ?? params.auto_deploy,
3855
3831
  database: params.database,
3832
+ docker_deploy_template_id: params.dockerDeployTemplateId ?? params.docker_deploy_template_id,
3856
3833
  metadata: params.metadata,
3857
3834
  ...attributionBody(params)
3858
3835
  });
@@ -3869,140 +3846,181 @@ var Deployments = class {
3869
3846
  * deployment so the control plane attaches it to the workspace Docker host.
3870
3847
  */
3871
3848
  async createDockerDeploy(params) {
3849
+ const templateId = params.dockerDeployTemplateId ?? params.docker_deploy_template_id;
3872
3850
  return this.create({
3873
3851
  ...params,
3874
- metadata: dockerDeployMetadata(params.metadata)
3852
+ metadata: dockerDeployMetadata(params.metadata, templateId)
3875
3853
  });
3876
3854
  }
3855
+ async listDockerDeployHosts(params = {}) {
3856
+ const data = await this.http.get(
3857
+ "/docker-deploy/hosts",
3858
+ stripUndefined10({
3859
+ workspace_id: params.workspaceId ?? params.workspace_id
3860
+ })
3861
+ );
3862
+ return listItems4(data, ["hosts", "items"]);
3863
+ }
3864
+ async ensureDockerDeployHost(params) {
3865
+ const data = await this.http.request("/docker-deploy/hosts/ensure", {
3866
+ method: "POST",
3867
+ body: stripUndefined10({
3868
+ workspace_id: params.workspaceId ?? params.workspace_id,
3869
+ region: params.region,
3870
+ size: params.size,
3871
+ appliance_image: params.applianceImage ?? params.appliance_image,
3872
+ metadata: params.metadata
3873
+ }),
3874
+ headers: { "Idempotency-Key": idempotencyKey4(params.idempotencyKey) }
3875
+ });
3876
+ return unwrap23(data);
3877
+ }
3878
+ async getDockerDeployHost(hostId) {
3879
+ const data = await this.http.get(`/docker-deploy/hosts/${hostId}`);
3880
+ return unwrap23(data);
3881
+ }
3877
3882
  /**
3878
- * Verify a Docker Deploy deployment before telling a user or agent it is
3879
- * live. Checks product markers, appliance host health, route metadata, and
3880
- * optionally probes the public URL.
3883
+ * Verify a Docker Deploy deployment end-to-end.
3884
+ *
3885
+ * This checks the deployment product marker, Docker Deploy host linkage,
3886
+ * appliance health, route runtime metadata, and optionally the public URL.
3887
+ * It is intentionally agent-friendly: use this after sandbox.deployDocker()
3888
+ * or createDockerDeploy() before telling a user the app is live.
3881
3889
  */
3882
3890
  async doctorDockerDeploy(deploymentId, params = {}) {
3883
3891
  const checks = [];
3884
3892
  const deployment = await this.get(deploymentId);
3885
- const metadata = deployment.metadata ?? {};
3886
- const product = dockerDeployProduct(deployment);
3887
- const hostId = dockerDeployHostId(deployment);
3893
+ const metadata = isRecord(deployment.metadata) ? deployment.metadata : {};
3894
+ const product = stringValue(deployment.deployment_product) ?? stringValue(metadata.deployment_product);
3895
+ const hostId = stringValue(deployment.docker_deploy_host_id) ?? stringValue(metadata.docker_deploy_host_id);
3888
3896
  addDoctorCheck(
3889
3897
  checks,
3890
3898
  "deployment_product",
3891
3899
  product === "docker_deploy",
3892
- product === "docker_deploy" ? "Deployment is marked for Docker Deploy." : `Expected deployment_product=docker_deploy, got ${String(product ?? "missing")}.`,
3893
- { deployment_product: product ?? null }
3900
+ product === "docker_deploy" ? "Deployment is marked as Docker Deploy." : `Expected deployment_product=docker_deploy, got ${product ?? "missing"}.`,
3901
+ { product: product ?? null }
3894
3902
  );
3895
3903
  addDoctorCheck(
3896
3904
  checks,
3897
3905
  "docker_deploy_host_id",
3898
- Boolean(hostId),
3899
- hostId ? "Deployment has a Docker Deploy host id." : "Deployment has no docker_deploy_host_id.",
3900
- { docker_deploy_host_id: hostId }
3906
+ !!hostId,
3907
+ hostId ? "Deployment is linked to a Docker Deploy appliance host." : "Deployment has no docker_deploy_host_id.",
3908
+ { docker_deploy_host_id: hostId ?? null }
3901
3909
  );
3902
3910
  let host;
3903
3911
  if (hostId) {
3904
3912
  try {
3905
- const rawHost = await this.http.get(
3906
- `/docker-deploy/hosts/${hostId}`
3907
- );
3908
- host = unwrap23(rawHost);
3913
+ host = await this.getDockerDeployHost(hostId);
3914
+ const status = host.status ?? null;
3915
+ const applianceStatus = host.appliance_status ?? null;
3916
+ const badHostStates = /* @__PURE__ */ new Set(["failed", "error", "destroyed"]);
3917
+ const badApplianceStates = /* @__PURE__ */ new Set(["failed", "error", "unhealthy"]);
3918
+ const ok = !badHostStates.has(String(status)) && !badApplianceStates.has(String(applianceStatus));
3909
3919
  addDoctorCheck(
3910
3920
  checks,
3911
3921
  "docker_deploy_host_health",
3912
- hostHealthy(host),
3913
- hostHealthy(host) ? "Docker Deploy host is active and healthy." : `Docker Deploy host status=${host.status} appliance=${host.appliance_status}.`,
3914
- {
3915
- status: host.status,
3916
- appliance_status: host.appliance_status
3917
- }
3922
+ ok,
3923
+ ok ? "Docker Deploy host is not reporting a failed state." : `Docker Deploy host is unhealthy: status=${status}, appliance_status=${applianceStatus}.`,
3924
+ { status, appliance_status: applianceStatus }
3918
3925
  );
3919
- } catch (error) {
3926
+ } catch (err) {
3920
3927
  addDoctorCheck(
3921
3928
  checks,
3922
3929
  "docker_deploy_host_health",
3923
3930
  false,
3924
- error instanceof Error ? error.message : String(error)
3931
+ `Could not fetch Docker Deploy host ${hostId}: ${err instanceof Error ? err.message : String(err)}`
3925
3932
  );
3926
3933
  }
3927
3934
  }
3928
- const app = dockerDeployApp(deployment);
3929
- const appPort = dockerDeployAppPort(app);
3930
- const appRunning = app?.["status"] === "running" && appPort !== null;
3935
+ const runtime = runtimeMetadata(metadata);
3936
+ const runtimeIp = runtime ? stringValue(runtime.ip) ?? stringValue(runtime.ip_address) : void 0;
3937
+ const runtimePort = runtime?.port;
3931
3938
  addDoctorCheck(
3932
3939
  checks,
3933
- "docker_deploy_app",
3934
- appRunning,
3935
- appRunning ? "Docker Deploy app metadata points at a running container." : "Deployment is missing running Docker Deploy app metadata.",
3936
- app ? {
3937
- app_id: app["app_id"],
3938
- container_id: app["container_id"],
3939
- status: app["status"],
3940
- url: app["url"],
3941
- expected_port: appPort
3942
- } : void 0
3943
- );
3944
- const runtime = metadata["runtime"];
3945
- const hasRuntimeRoute = typeof runtime === "object" && runtime !== null && typeof runtime["ip"] === "string" && typeof runtime["port"] === "number";
3946
- const runtimeRecord = hasRuntimeRoute && typeof runtime === "object" ? runtime : void 0;
3947
- const routeMatchesContainerPort = hasRuntimeRoute && (appPort === null || runtimeRecord?.["port"] === appPort);
3940
+ "route_runtime",
3941
+ !!runtimeIp && runtimePort !== void 0 && runtimePort !== null,
3942
+ runtimeIp && runtimePort !== void 0 && runtimePort !== null ? "Deployment route metadata points at a runtime target." : "Deployment metadata has no runtime ip/port target.",
3943
+ { ip: runtimeIp ?? null, port: runtimePort ?? null }
3944
+ );
3945
+ const publicUrl = stringValue(deployment.public_url) ?? stringValue(metadata.public_url);
3948
3946
  addDoctorCheck(
3949
3947
  checks,
3950
- "runtime_route",
3951
- hasRuntimeRoute && routeMatchesContainerPort,
3952
- hasRuntimeRoute && routeMatchesContainerPort ? "Deployment route points at the Docker container host port." : hasRuntimeRoute && appPort !== null ? `Deployment route port ${String(runtimeRecord?.["port"])} does not match Docker container host port ${appPort}.` : "Deployment is missing appliance runtime route metadata.",
3953
- runtimeRecord ? {
3954
- ...runtimeRecord,
3955
- expected_port: appPort,
3956
- docker_deploy_url: app?.["url"]
3957
- } : void 0
3948
+ "public_url",
3949
+ !!publicUrl,
3950
+ publicUrl ? "Deployment has a public URL." : "Deployment has no public URL to probe.",
3951
+ { public_url: publicUrl ?? null }
3958
3952
  );
3959
3953
  let probe;
3960
- const publicUrl = deployment.public_url;
3961
- const path = params.probePath ?? params.probe_path ?? "/";
3962
- if (publicUrl && typeof fetch === "function") {
3963
- const url = probeUrl(publicUrl, path);
3954
+ if (params.probe !== false && publicUrl) {
3955
+ const fetchImpl = params.fetchImpl ?? fetch;
3956
+ const url = publicProbeUrl(publicUrl, params.probePath ?? "/");
3964
3957
  const controller = new AbortController();
3965
- const timeout = setTimeout(
3966
- () => controller.abort(),
3967
- params.timeoutMs ?? params.timeout_ms ?? 1e4
3968
- );
3958
+ const timer = setTimeout(() => controller.abort(), params.timeoutMs ?? 2e4);
3969
3959
  try {
3970
- const response = await fetch(url, {
3971
- method: "GET",
3960
+ const response = await fetchImpl(url, {
3961
+ headers: { Accept: "*/*" },
3972
3962
  signal: controller.signal
3973
3963
  });
3974
- probe = { url, ok: response.ok, status: response.status };
3964
+ const text = await response.text();
3965
+ const gatewayJson = looksLikeMiosaGatewayJson(text);
3966
+ const ok = response.ok && !gatewayJson;
3967
+ probe = {
3968
+ url,
3969
+ status: response.status,
3970
+ ok,
3971
+ responseSnippet: text.slice(0, 500)
3972
+ };
3973
+ if (gatewayJson) probe.gatewayJson = true;
3975
3974
  addDoctorCheck(
3976
3975
  checks,
3977
3976
  "public_url_probe",
3978
- response.ok,
3979
- response.ok ? `Public URL returned HTTP ${response.status}.` : `Public URL returned HTTP ${response.status}.`,
3980
- { url, status: response.status }
3977
+ ok,
3978
+ ok ? `Public URL returned HTTP ${response.status}.` : gatewayJson ? "Public URL returned MIOSA gateway JSON instead of the app." : `Public URL returned HTTP ${response.status}.`,
3979
+ { status: response.status, gateway_json: gatewayJson }
3981
3980
  );
3982
- } catch (error) {
3983
- probe = {
3984
- url,
3985
- ok: false,
3986
- error: error instanceof Error ? error.message : String(error)
3987
- };
3981
+ } catch (err) {
3982
+ const message = err instanceof Error ? err.message : String(err);
3983
+ probe = { url, status: null, ok: false, responseSnippet: message };
3988
3984
  addDoctorCheck(
3989
3985
  checks,
3990
3986
  "public_url_probe",
3991
3987
  false,
3992
- probe.error ?? "Public URL probe failed.",
3993
- { url }
3988
+ `Public URL probe failed: ${message}`
3994
3989
  );
3995
3990
  } finally {
3996
- clearTimeout(timeout);
3991
+ clearTimeout(timer);
3997
3992
  }
3998
3993
  }
3999
- return {
3994
+ const result = {
4000
3995
  ok: checks.every((check) => check.ok),
4001
3996
  deployment,
4002
- ...host ? { host } : {},
4003
- checks,
4004
- ...probe ? { probe } : {}
3997
+ checks
4005
3998
  };
3999
+ if (host) result.host = host;
4000
+ if (publicUrl !== void 0) result.publicUrl = publicUrl;
4001
+ if (probe) result.probe = probe;
4002
+ return result;
4003
+ }
4004
+ async listDockerDeployTemplates(params = {}) {
4005
+ const query = {
4006
+ category: params.category,
4007
+ runtime: params.runtime,
4008
+ framework: params.framework,
4009
+ include_preview: params.includePreview ?? params.include_preview
4010
+ };
4011
+ const data = await this.http.get(
4012
+ "/docker-deploy/templates",
4013
+ stripUndefined10(query)
4014
+ );
4015
+ const templates = unwrap23(data);
4016
+ if (Array.isArray(templates)) return templates;
4017
+ return (templates?.templates ?? templates?.items ?? templates?.data ?? []) || [];
4018
+ }
4019
+ async getDockerDeployTemplate(templateId) {
4020
+ const data = await this.http.get(
4021
+ `/docker-deploy/templates/${templateId}`
4022
+ );
4023
+ return unwrap23(data);
4006
4024
  }
4007
4025
  async update(deploymentId, params) {
4008
4026
  const body = stripUndefined10({
@@ -4026,7 +4044,15 @@ var Deployments = class {
4026
4044
  source_sandbox_id: params.sourceSandboxId ?? params.source_sandbox_id,
4027
4045
  output_path: params.outputPath ?? params.output_path,
4028
4046
  entrypoint: params.entrypoint,
4029
- promote: params.promote
4047
+ promote: params.promote,
4048
+ build_command: params.buildCommand ?? params.build_command,
4049
+ run_command: params.runCommand ?? params.run_command,
4050
+ port: params.port,
4051
+ health_check_path: params.healthCheckPath ?? params.health_check_path,
4052
+ deployment_type: params.deploymentType ?? params.deployment_type,
4053
+ docker_deploy_template_id: params.dockerDeployTemplateId ?? params.docker_deploy_template_id,
4054
+ data_services: params.dataServices ?? params.data_services,
4055
+ ...attributionBody(params)
4030
4056
  });
4031
4057
  const data = await this.http.request(
4032
4058
  `/deployments/${deploymentId}/publish`,
@@ -4051,7 +4077,15 @@ var Deployments = class {
4051
4077
  source_snapshot_path: params.sourceSnapshotPath ?? params.source_snapshot_path,
4052
4078
  entrypoint: params.entrypoint,
4053
4079
  domain: params.domain,
4054
- custom_domain: params.customDomain ?? params.custom_domain
4080
+ custom_domain: params.customDomain ?? params.custom_domain,
4081
+ build_command: params.buildCommand ?? params.build_command,
4082
+ run_command: params.runCommand ?? params.run_command,
4083
+ port: params.port,
4084
+ health_check_path: params.healthCheckPath ?? params.health_check_path,
4085
+ deployment_type: params.deploymentType ?? params.deployment_type,
4086
+ docker_deploy_template_id: params.dockerDeployTemplateId ?? params.docker_deploy_template_id,
4087
+ data_services: params.dataServices ?? params.data_services,
4088
+ ...attributionBody(params)
4055
4089
  });
4056
4090
  const data = await this.http.request(
4057
4091
  `/sandboxes/${sandboxId}/deploy`,
@@ -4120,89 +4154,205 @@ var Deployments = class {
4120
4154
  }
4121
4155
  };
4122
4156
 
4123
- // src/resources/docker-deploy.ts
4124
- function workspaceId(params) {
4125
- return params?.workspace_id ?? params?.workspaceId;
4126
- }
4127
- function ensureBody(params) {
4128
- const body = {};
4129
- const id = params.workspace_id ?? params.workspaceId;
4130
- const externalId = params.external_workspace_id ?? params.externalWorkspaceId;
4131
- if (id) body.workspace_id = id;
4132
- if (externalId) body.external_workspace_id = externalId;
4133
- return body;
4134
- }
4135
- function unwrapHost(response) {
4136
- const host = response.data ?? response.host;
4137
- if (!host) {
4138
- throw new Error("Docker Deploy host response was empty.");
4139
- }
4140
- return host;
4141
- }
4142
- function unwrapTemplates(response) {
4143
- return response.data ?? response.templates ?? [];
4144
- }
4145
- function unwrapTemplate(response) {
4146
- const template = response.data ?? response.template;
4147
- if (!template) {
4148
- throw new Error("Docker Deploy template response was empty.");
4149
- }
4150
- return template;
4151
- }
4152
- var DockerDeploy = class {
4157
+ // src/resources/devices.ts
4158
+ var DEVICE_CATALOG = [
4159
+ {
4160
+ kind: "sandbox_worker",
4161
+ label: "Sandbox Worker",
4162
+ purpose: "Isolated Linux workspace for agents to create files, run code, preview apps, snapshot, fork, and publish.",
4163
+ lifecycle: "Persistent by default; use stop/resume/snapshot/fork where the account backend supports saved state.",
4164
+ persistence: "Use one-hour timeouts for interactive builds and checkpoint before long pauses.",
4165
+ primaryCommands: [
4166
+ "miosa.sandboxes.create({ templateId: 'nextjs', timeoutSec: 3600 })",
4167
+ "sandbox.exec.run('codex ...', { cwd: '/workspace' })",
4168
+ "sandbox.files.write('/workspace/app/page.jsx', source)",
4169
+ "miosa.deployments.publishFromSandbox(...)"
4170
+ ],
4171
+ useWhen: [
4172
+ "Coding agents should build inside the remote filesystem.",
4173
+ "You need command execution, file writes, package installs, previews, artifacts, or app publish.",
4174
+ "You want virtual-device behavior without a GUI desktop."
4175
+ ],
4176
+ avoidWhen: [
4177
+ "The workflow requires full browser/desktop control.",
4178
+ "The app is ready for production; publish it to a deployment runtime."
4179
+ ]
4180
+ },
4181
+ {
4182
+ kind: "computer",
4183
+ label: "Computer",
4184
+ purpose: "Durable VM/desktop device for browser automation, CUA sessions, SSH, tunnels, and persistent agent control.",
4185
+ lifecycle: "Managed as a Computer with desktop/browser and operator-style control surfaces.",
4186
+ persistence: "Use checkpoints, volumes, tunnels, and agent sessions for long-lived desktop workflows.",
4187
+ primaryCommands: [
4188
+ "miosa.computers.create({ name: 'browser-agent' })",
4189
+ "computer.exec.run('npm test')",
4190
+ "computer.desktop.open()"
4191
+ ],
4192
+ useWhen: [
4193
+ "The agent needs Chromium or a full desktop.",
4194
+ "The workflow logs into dashboards, fills forms, clicks buttons, or captures screenshots.",
4195
+ "A human and agent share the same persistent machine state."
4196
+ ],
4197
+ avoidWhen: [
4198
+ "Simple code generation/build/test work fits a cheaper sandbox worker.",
4199
+ "You only need durable app hosting."
4200
+ ]
4201
+ },
4202
+ {
4203
+ kind: "local_device",
4204
+ label: "Local Device",
4205
+ purpose: "Developer-owned machine connected through CLI/MCP for local discovery and private tooling.",
4206
+ lifecycle: "Not hosted by MIOSA; the user owns uptime and state.",
4207
+ persistence: "State is local machine state. Do not assume cloud resume semantics.",
4208
+ primaryCommands: ["miosa mcp install", "miosa doctor --json"],
4209
+ useWhen: [
4210
+ "The agent needs local repository discovery before cloud execution.",
4211
+ "The user intentionally wants local private tools."
4212
+ ],
4213
+ avoidWhen: [
4214
+ "Customer code must stay isolated in MIOSA-hosted infrastructure.",
4215
+ "The workflow needs reproducible shared cloud state."
4216
+ ]
4217
+ },
4218
+ {
4219
+ kind: "docker_deploy_host",
4220
+ label: "Docker Deploy Host",
4221
+ purpose: "Workspace appliance VM that runs Docker containers for durable apps published from sandboxes.",
4222
+ lifecycle: "Always-on deployment capacity; not an interactive coding workspace.",
4223
+ persistence: "Versioned releases and routing are durable; edits happen in sandboxes before publish.",
4224
+ primaryCommands: [
4225
+ "miosa sandbox publish <id> --docker-deploy",
4226
+ "miosa deploy --docker-deploy"
4227
+ ],
4228
+ useWhen: [
4229
+ "You need many small apps, APIs, funnels, or client sites in one workspace appliance.",
4230
+ "You want stable public URLs backed by Docker containers."
4231
+ ],
4232
+ avoidWhen: [
4233
+ "Interactive agent work is still happening.",
4234
+ "The app needs the standard MIOSA Deploy runtime."
4235
+ ]
4236
+ }
4237
+ ];
4238
+ var Devices = class {
4239
+ http;
4153
4240
  constructor(http) {
4154
4241
  this.http = http;
4155
4242
  }
4156
- http;
4157
4243
  /**
4158
- * List Docker Deploy appliance hosts scoped to the current tenant.
4159
- *
4160
- * Pass a workspace ID to inspect the dedicated always-on appliance machine
4161
- * for one white-label workspace.
4244
+ * Return the static device catalog used by orchestration apps to choose the
4245
+ * right MIOSA execution surface before creating resources.
4162
4246
  */
4163
- async listHosts(params = {}) {
4164
- const res = await this.http.get(
4165
- "/docker-deploy/hosts",
4166
- { workspace_id: workspaceId(params) }
4167
- );
4168
- return res.data ?? res.hosts ?? [];
4247
+ catalog() {
4248
+ return DEVICE_CATALOG.map((entry) => ({ ...entry }));
4169
4249
  }
4170
4250
  /**
4171
- * Ensure a workspace has its dedicated Docker Deploy appliance host.
4172
- *
4173
- * The host may still be `pending`, `provisioning`, or `bootstrapping` after
4174
- * this call. Treat `status === "active"` and `appliance_status === "healthy"`
4175
- * as the ready condition before sending app/container traffic to it.
4251
+ * List hosted devices by normalizing existing sandboxes and computers.
4252
+ * Partial backend failures are returned in `errors` so orchestration UIs can
4253
+ * still show usable inventory instead of failing the whole page.
4176
4254
  */
4177
- async ensureHost(params = {}) {
4178
- const res = await this.http.post(
4179
- "/docker-deploy/hosts/ensure",
4180
- ensureBody(params)
4181
- );
4182
- return { host: unwrapHost(res), queued: res.queued ?? false };
4183
- }
4184
- /** Fetch one Docker Deploy host by ID. */
4185
- async getHost(hostId) {
4186
- const res = await this.http.get(
4187
- `/docker-deploy/hosts/${hostId}`
4188
- );
4189
- return unwrapHost(res);
4190
- }
4191
- /** List Docker Deploy starter templates. */
4192
- async listTemplates() {
4193
- const res = await this.http.get(
4194
- "/docker-deploy/templates"
4195
- );
4196
- return unwrapTemplates(res);
4197
- }
4198
- /** Fetch one Docker Deploy starter template by ID. */
4199
- async getTemplate(templateId) {
4200
- const res = await this.http.get(
4201
- `/docker-deploy/templates/${encodeURIComponent(templateId)}`
4202
- );
4203
- return unwrapTemplate(res);
4255
+ async list(params = {}) {
4256
+ const kind = normalizeKind(params.kind ?? "all");
4257
+ const devices = [];
4258
+ const errors = [];
4259
+ if (kind === "all" || kind === "sandbox_worker") {
4260
+ try {
4261
+ const sandboxes = await this.http.get("/sandboxes");
4262
+ devices.push(...unwrapList11(sandboxes, ["sandboxes"]).map(normalizeSandbox));
4263
+ } catch (err) {
4264
+ errors.push(toListError("sandboxes", err));
4265
+ }
4266
+ }
4267
+ if (kind === "all" || kind === "computer") {
4268
+ try {
4269
+ const computers = await this.http.get("/computers");
4270
+ devices.push(...unwrapList11(computers, ["computers"]).map(normalizeComputer));
4271
+ } catch (err) {
4272
+ errors.push(toListError("computers", err));
4273
+ }
4274
+ }
4275
+ return { devices, errors };
4204
4276
  }
4205
4277
  };
4278
+ function normalizeKind(kind) {
4279
+ if (kind === "all") return "all";
4280
+ if (kind === "sandbox" || kind === "sandbox_worker") {
4281
+ return "sandbox_worker";
4282
+ }
4283
+ if (kind === "computer") return "computer";
4284
+ throw new Error(`Unsupported device kind: ${kind}`);
4285
+ }
4286
+ function normalizeSandbox(row) {
4287
+ return compactRecord({
4288
+ id: stringField(row, "id"),
4289
+ kind: "sandbox_worker",
4290
+ source: "sandboxes",
4291
+ name: optionalString(row, "name"),
4292
+ state: optionalString(row, "state") ?? optionalString(row, "status"),
4293
+ ready: optionalBoolean(row, "ready"),
4294
+ persistent: optionalBoolean(row, "persistent"),
4295
+ alwaysOn: optionalBoolean(row, "always_on"),
4296
+ template: optionalString(row, "template_id") ?? optionalString(row, "template"),
4297
+ previewUrl: optionalString(row, "preview_url"),
4298
+ timeoutRemainingMs: optionalNumber(row, "timeout_remaining_ms")
4299
+ });
4300
+ }
4301
+ function normalizeComputer(row) {
4302
+ return compactRecord({
4303
+ id: stringField(row, "id"),
4304
+ kind: "computer",
4305
+ source: "computers",
4306
+ name: optionalString(row, "name"),
4307
+ state: optionalString(row, "status") ?? optionalString(row, "state"),
4308
+ ready: optionalBoolean(row, "ready"),
4309
+ region: optionalString(row, "region"),
4310
+ template: optionalString(row, "template_type") ?? optionalString(row, "template")
4311
+ });
4312
+ }
4313
+ function compactRecord(record) {
4314
+ return Object.fromEntries(
4315
+ Object.entries(record).filter(([, value]) => value !== void 0)
4316
+ );
4317
+ }
4318
+ function unwrapList11(payload, keys) {
4319
+ const value = isRecord2(payload) && "data" in payload ? payload.data : payload;
4320
+ if (Array.isArray(value)) return value.filter(isRecord2);
4321
+ if (isRecord2(value)) {
4322
+ for (const key of keys) {
4323
+ const nested = value[key];
4324
+ if (Array.isArray(nested)) return nested.filter(isRecord2);
4325
+ }
4326
+ }
4327
+ return [];
4328
+ }
4329
+ function toListError(source, err) {
4330
+ const message = err instanceof Error ? err.message : String(err);
4331
+ return {
4332
+ source,
4333
+ message,
4334
+ retryable: /fetch failed|ECONNRESET|HTTP 502|other side closed|socket hang up|bad gateway/i.test(
4335
+ message
4336
+ )
4337
+ };
4338
+ }
4339
+ function stringField(row, key) {
4340
+ const value = row[key];
4341
+ return typeof value === "string" ? value : String(value ?? "");
4342
+ }
4343
+ function optionalString(row, key) {
4344
+ const value = row[key];
4345
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4346
+ }
4347
+ function optionalBoolean(row, key) {
4348
+ return typeof row[key] === "boolean" ? row[key] : void 0;
4349
+ }
4350
+ function optionalNumber(row, key) {
4351
+ return typeof row[key] === "number" ? row[key] : void 0;
4352
+ }
4353
+ function isRecord2(value) {
4354
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4355
+ }
4206
4356
 
4207
4357
  // src/resources/email.ts
4208
4358
  function unwrap24(data) {
@@ -4221,7 +4371,7 @@ function unwrap24(data) {
4221
4371
  }
4222
4372
  return data;
4223
4373
  }
4224
- function unwrapList11(data) {
4374
+ function unwrapList12(data) {
4225
4375
  if (Array.isArray(data)) return data;
4226
4376
  if (data && typeof data === "object") {
4227
4377
  const d = data;
@@ -4249,7 +4399,7 @@ var EmailCampaigns = class {
4249
4399
  }
4250
4400
  http;
4251
4401
  async list(filters = {}) {
4252
- return unwrapList11(
4402
+ return unwrapList12(
4253
4403
  await this.http.get(
4254
4404
  "/admin/email-campaigns",
4255
4405
  filters
@@ -4285,7 +4435,7 @@ var EmailCampaigns = class {
4285
4435
  );
4286
4436
  }
4287
4437
  async deliveries(campaignId, filters = {}) {
4288
- return unwrapList11(
4438
+ return unwrapList12(
4289
4439
  await this.http.get(
4290
4440
  `/admin/email-campaigns/${campaignId}/deliveries`,
4291
4441
  filters
@@ -4299,7 +4449,7 @@ var EmailTemplates = class {
4299
4449
  }
4300
4450
  http;
4301
4451
  async list(filters = {}) {
4302
- return unwrapList11(
4452
+ return unwrapList12(
4303
4453
  await this.http.get("/admin/email-templates", filters)
4304
4454
  );
4305
4455
  }
@@ -4331,7 +4481,7 @@ var EmailInbox = class {
4331
4481
  }
4332
4482
  http;
4333
4483
  async list(filters = {}) {
4334
- return unwrapList11(
4484
+ return unwrapList12(
4335
4485
  await this.http.get("/admin/email-inbox", filters)
4336
4486
  );
4337
4487
  }
@@ -5355,49 +5505,49 @@ var OcWorkspaces = class {
5355
5505
  /**
5356
5506
  * Fetch a single workspace.
5357
5507
  */
5358
- async get(hostId, workspaceId2) {
5508
+ async get(hostId, workspaceId) {
5359
5509
  return this.http.get(
5360
- `${this.base(hostId)}/${workspaceId2}`
5510
+ `${this.base(hostId)}/${workspaceId}`
5361
5511
  );
5362
5512
  }
5363
5513
  /**
5364
5514
  * Update workspace metadata (name, branch).
5365
5515
  */
5366
- async update(hostId, workspaceId2, params) {
5516
+ async update(hostId, workspaceId, params) {
5367
5517
  return this.http.patch(
5368
- `${this.base(hostId)}/${workspaceId2}`,
5518
+ `${this.base(hostId)}/${workspaceId}`,
5369
5519
  params
5370
5520
  );
5371
5521
  }
5372
5522
  /**
5373
5523
  * Delete a workspace.
5374
5524
  */
5375
- async delete(hostId, workspaceId2) {
5376
- return this.http.delete(`${this.base(hostId)}/${workspaceId2}`);
5525
+ async delete(hostId, workspaceId) {
5526
+ return this.http.delete(`${this.base(hostId)}/${workspaceId}`);
5377
5527
  }
5378
5528
  /**
5379
5529
  * Pull the latest changes from the remote repository.
5380
5530
  */
5381
- async pull(hostId, workspaceId2) {
5531
+ async pull(hostId, workspaceId) {
5382
5532
  return this.http.post(
5383
- `${this.base(hostId)}/${workspaceId2}/pull`
5533
+ `${this.base(hostId)}/${workspaceId}/pull`
5384
5534
  );
5385
5535
  }
5386
5536
  /**
5387
5537
  * Open a terminal session scoped to the workspace root directory.
5388
5538
  * Returns a short-lived WebSocket ticket.
5389
5539
  */
5390
- async openTerminal(hostId, workspaceId2) {
5540
+ async openTerminal(hostId, workspaceId) {
5391
5541
  return this.http.post(
5392
- `${this.base(hostId)}/${workspaceId2}/open-terminal`
5542
+ `${this.base(hostId)}/${workspaceId}/open-terminal`
5393
5543
  );
5394
5544
  }
5395
5545
  /**
5396
5546
  * Stream workspace setup / clone / install events.
5397
5547
  */
5398
- events(hostId, workspaceId2) {
5548
+ events(hostId, workspaceId) {
5399
5549
  return this.http.stream(
5400
- `${this.base(hostId)}/${workspaceId2}/events`
5550
+ `${this.base(hostId)}/${workspaceId}/events`
5401
5551
  );
5402
5552
  }
5403
5553
  };
@@ -6324,6 +6474,7 @@ var Sandbox = class _Sandbox {
6324
6474
  port: params.port,
6325
6475
  health_check_path: params.healthCheckPath ?? params.health_check_path,
6326
6476
  deployment_type: params.deploymentType ?? params.deployment_type,
6477
+ docker_deploy_template_id: params.dockerDeployTemplateId ?? params.docker_deploy_template_id,
6327
6478
  type: params.type,
6328
6479
  mode: params.mode,
6329
6480
  database: params.database,
@@ -6788,7 +6939,7 @@ function unwrap39(data) {
6788
6939
  }
6789
6940
  return data;
6790
6941
  }
6791
- function unwrapList12(data) {
6942
+ function unwrapList13(data) {
6792
6943
  if (Array.isArray(data)) return data;
6793
6944
  if (data && typeof data === "object") {
6794
6945
  const d = data;
@@ -6807,7 +6958,7 @@ var SnapshotsStandalone = class {
6807
6958
  const query = Object.fromEntries(
6808
6959
  Object.entries(filters).filter(([, v]) => v !== void 0)
6809
6960
  );
6810
- return unwrapList12(await this.http.get("/admin/snapshots", query));
6961
+ return unwrapList13(await this.http.get("/admin/snapshots", query));
6811
6962
  }
6812
6963
  async get(snapshotId) {
6813
6964
  return unwrap39(
@@ -7003,96 +7154,22 @@ var OrgInvites = class {
7003
7154
  function unwrap41(payload) {
7004
7155
  if (payload && typeof payload === "object") {
7005
7156
  const p = payload;
7006
- for (const k of ["data", "tenant", "branding", "items"]) {
7157
+ for (const k of ["data", "tenant", "items"]) {
7007
7158
  if (k in p) return p[k];
7008
7159
  }
7009
7160
  }
7010
7161
  return payload;
7011
7162
  }
7012
- var PreviewDomain = class {
7013
- constructor(http) {
7014
- this.http = http;
7015
- }
7016
- http;
7017
- /** Get the tenant's white-label preview domain settings. */
7018
- async get() {
7019
- const data = await this.http.get("/tenant/preview-domain");
7020
- return unwrap41(data);
7021
- }
7022
- /** Set the tenant's white-label preview domain. */
7023
- async set(domain) {
7024
- const data = await this.http.put("/tenant/preview-domain", {
7025
- preview_domain: domain
7026
- });
7027
- return unwrap41(data);
7028
- }
7029
- /** Re-run DNS verification for the configured preview domain. */
7030
- async verify() {
7031
- const data = await this.http.post(
7032
- "/tenant/preview-domain/verify",
7033
- {}
7034
- );
7035
- return unwrap41(data);
7036
- }
7037
- /** Remove the tenant's custom preview domain. */
7038
- async delete() {
7039
- await this.http.delete("/tenant/preview-domain");
7040
- }
7041
- };
7042
- var Branding = class {
7043
- constructor(http) {
7044
- this.http = http;
7045
- }
7046
- http;
7047
- /** Get tenant branding used by white-label hosted surfaces. */
7048
- async get() {
7049
- const data = await this.http.get("/tenant/branding");
7050
- return unwrap41(data);
7051
- }
7052
- /** Update tenant branding used by white-label hosted surfaces. */
7053
- async set(params) {
7054
- const body = Object.fromEntries(
7055
- Object.entries(params).filter(([, v]) => v !== void 0)
7056
- );
7057
- const data = await this.http.put("/tenant/branding", {
7058
- branding: body
7059
- });
7060
- return unwrap41(data);
7061
- }
7062
- /** Reset tenant branding to platform defaults. */
7063
- async delete() {
7064
- await this.http.delete("/tenant/branding");
7065
- }
7066
- };
7067
7163
  var Tenant = class {
7068
- http;
7069
- preview_domain;
7070
- branding;
7071
- /** camelCase alias for SDK consumers that avoid snake_case properties. */
7072
- previewDomain;
7073
7164
  constructor(http) {
7074
7165
  this.http = http;
7075
- this.preview_domain = new PreviewDomain(http);
7076
- this.previewDomain = this.preview_domain;
7077
- this.branding = new Branding(http);
7078
7166
  }
7167
+ http;
7079
7168
  /** Get the current tenant's plan, limits, and live usage counters. */
7080
7169
  async current() {
7081
7170
  const data = await this.http.get("/tenant/plan");
7082
7171
  return unwrap41(data);
7083
7172
  }
7084
- /** Convenience alias for `tenant.branding.get()`. */
7085
- async getBranding() {
7086
- return this.branding.get();
7087
- }
7088
- /** Convenience alias for `tenant.branding.set(...)`. */
7089
- async setBranding(params) {
7090
- return this.branding.set(params);
7091
- }
7092
- /** Convenience alias for `tenant.branding.delete()`. */
7093
- async deleteBranding() {
7094
- await this.branding.delete();
7095
- }
7096
7173
  };
7097
7174
 
7098
7175
  // src/resources/usage.ts
@@ -7214,35 +7291,43 @@ function stripUndefined25(input) {
7214
7291
  function idempotencyKey10(key) {
7215
7292
  return key ?? randomUUID();
7216
7293
  }
7217
- function bodyBuffer(body) {
7218
- if (Buffer.isBuffer(body)) return body;
7219
- if (typeof body === "string") return Buffer.from(body, "utf8");
7220
- return Buffer.from(body);
7221
- }
7222
- function verifySignature(body, header, secret, toleranceSec = 300) {
7223
- const parts = Object.fromEntries(
7224
- header.split(",").map((chunk) => chunk.split("=", 2)).filter(([key, value]) => key && value)
7225
- );
7226
- const timestamp = parts.t;
7227
- const received = parts.v1;
7228
- if (!timestamp || !received || !secret) return false;
7229
- const unixSeconds = Number(timestamp);
7230
- if (!Number.isFinite(unixSeconds)) return false;
7231
- const ageSec = Math.abs(Date.now() / 1e3 - unixSeconds);
7232
- if (ageSec > toleranceSec) {
7233
- throw new Error("webhook timestamp too old");
7234
- }
7235
- const rawBody = bodyBuffer(body);
7236
- const signed = Buffer.concat([Buffer.from(`${timestamp}.`), rawBody]);
7237
- const expected = createHmac("sha256", secret).update(signed).digest("hex");
7238
- try {
7239
- return timingSafeEqual(
7240
- Buffer.from(expected, "utf8"),
7241
- Buffer.from(received, "utf8")
7242
- );
7243
- } catch {
7244
- return false;
7294
+ function parseSignatureHeader(header) {
7295
+ const parts = header.split(",").map((part) => part.trim());
7296
+ let timestamp = null;
7297
+ const signatures = [];
7298
+ for (const part of parts) {
7299
+ const [key, value] = part.split("=", 2);
7300
+ if (!key || !value) continue;
7301
+ if (key === "t") {
7302
+ const parsed = Number(value);
7303
+ if (Number.isFinite(parsed)) timestamp = parsed;
7304
+ } else if (key === "v1") {
7305
+ signatures.push(value);
7306
+ }
7245
7307
  }
7308
+ if (timestamp == null || signatures.length === 0) return null;
7309
+ return { timestamp, signatures };
7310
+ }
7311
+ function verifySignature(body, header, secret, options = {}) {
7312
+ const parsed = parseSignatureHeader(header);
7313
+ if (!parsed) return false;
7314
+ const toleranceSeconds = options.toleranceSeconds ?? 300;
7315
+ const ageSeconds = Math.abs(Math.floor(Date.now() / 1e3) - parsed.timestamp);
7316
+ if (ageSeconds > toleranceSeconds) {
7317
+ throw new Error("Webhook signature timestamp is too old");
7318
+ }
7319
+ const bodyBuffer = Buffer.isBuffer(body) ? body : Buffer.from(body);
7320
+ const signedPayload = Buffer.concat([
7321
+ Buffer.from(`${parsed.timestamp}.`),
7322
+ bodyBuffer
7323
+ ]);
7324
+ const expected = createHmac("sha256", secret).update(signedPayload).digest("hex");
7325
+ const expectedBuffer = Buffer.from(expected, "hex");
7326
+ return parsed.signatures.some((signature) => {
7327
+ const actualBuffer = Buffer.from(signature, "hex");
7328
+ if (actualBuffer.length !== expectedBuffer.length) return false;
7329
+ return timingSafeEqual(actualBuffer, expectedBuffer);
7330
+ });
7246
7331
  }
7247
7332
  var Webhooks = class {
7248
7333
  constructor(http) {
@@ -7250,6 +7335,7 @@ var Webhooks = class {
7250
7335
  }
7251
7336
  http;
7252
7337
  static verifySignature = verifySignature;
7338
+ static verify_signature = verifySignature;
7253
7339
  async list(params = {}) {
7254
7340
  const query = stripUndefined25({ ...params });
7255
7341
  const data = await this.http.get("/webhooks", query);
@@ -7314,9 +7400,9 @@ var WorkspaceInvites = class {
7314
7400
  *
7315
7401
  * `POST /workspaces/:id/invites`
7316
7402
  */
7317
- async create(workspaceId2, params) {
7403
+ async create(workspaceId, params) {
7318
7404
  return this.http.post(
7319
- `/workspaces/${workspaceId2}/invites`,
7405
+ `/workspaces/${workspaceId}/invites`,
7320
7406
  params
7321
7407
  );
7322
7408
  }
@@ -7325,9 +7411,9 @@ var WorkspaceInvites = class {
7325
7411
  *
7326
7412
  * `GET /workspaces/:id/invites`
7327
7413
  */
7328
- async list(workspaceId2) {
7414
+ async list(workspaceId) {
7329
7415
  const res = await this.http.get(
7330
- `/workspaces/${workspaceId2}/invites`
7416
+ `/workspaces/${workspaceId}/invites`
7331
7417
  );
7332
7418
  return res.data ?? [];
7333
7419
  }
@@ -7339,9 +7425,9 @@ var WorkspaceInvites = class {
7339
7425
  *
7340
7426
  * `DELETE /workspaces/:id/invites/:invite_id`
7341
7427
  */
7342
- async revoke(workspaceId2, inviteId) {
7428
+ async revoke(workspaceId, inviteId) {
7343
7429
  return this.http.delete(
7344
- `/workspaces/${workspaceId2}/invites/${inviteId}`
7430
+ `/workspaces/${workspaceId}/invites/${inviteId}`
7345
7431
  );
7346
7432
  }
7347
7433
  /**
@@ -7395,9 +7481,9 @@ var WorkspaceMembers = class {
7395
7481
  *
7396
7482
  * `GET /workspaces/:id/members`
7397
7483
  */
7398
- async list(workspaceId2) {
7484
+ async list(workspaceId) {
7399
7485
  const res = await this.http.get(
7400
- `/workspaces/${workspaceId2}/members`
7486
+ `/workspaces/${workspaceId}/members`
7401
7487
  );
7402
7488
  return res.data ?? res;
7403
7489
  }
@@ -7413,9 +7499,9 @@ var WorkspaceMembers = class {
7413
7499
  * @throws `MiosaError` with code `NOT_TENANT_MEMBER` if the user is not an
7414
7500
  * org member.
7415
7501
  */
7416
- async add(workspaceId2, params) {
7502
+ async add(workspaceId, params) {
7417
7503
  const res = await this.http.post(
7418
- `/workspaces/${workspaceId2}/members`,
7504
+ `/workspaces/${workspaceId}/members`,
7419
7505
  params
7420
7506
  );
7421
7507
  return res.data ?? res;
@@ -7425,9 +7511,9 @@ var WorkspaceMembers = class {
7425
7511
  *
7426
7512
  * `PATCH /workspaces/:id/members/:user_id`
7427
7513
  */
7428
- async updateRole(workspaceId2, userId, params) {
7514
+ async updateRole(workspaceId, userId, params) {
7429
7515
  const res = await this.http.patch(
7430
- `/workspaces/${workspaceId2}/members/${userId}`,
7516
+ `/workspaces/${workspaceId}/members/${userId}`,
7431
7517
  params
7432
7518
  );
7433
7519
  return res.data ?? res;
@@ -7442,9 +7528,9 @@ var WorkspaceMembers = class {
7442
7528
  *
7443
7529
  * @throws `MiosaError` with code `LAST_OWNER` if the target is the sole owner.
7444
7530
  */
7445
- async remove(workspaceId2, userId) {
7531
+ async remove(workspaceId, userId) {
7446
7532
  return this.http.delete(
7447
- `/workspaces/${workspaceId2}/members/${userId}`
7533
+ `/workspaces/${workspaceId}/members/${userId}`
7448
7534
  );
7449
7535
  }
7450
7536
  };
@@ -7496,13 +7582,13 @@ var Miosa = class {
7496
7582
  computers;
7497
7583
  /** Sandboxes — native code-execution environments under `/sandboxes`. */
7498
7584
  sandboxes;
7585
+ /** Agent device facade — route work across Sandboxes, Computers, and deploy hosts. */
7586
+ devices;
7499
7587
  /**
7500
7588
  * Deployments — publish from a sandbox to a stable production URL.
7501
7589
  * Versions, releases, rollback, custom domains.
7502
7590
  */
7503
7591
  deployments;
7504
- /** Docker Deploy appliance hosts — one always-on workspace host, many apps. */
7505
- dockerDeploy;
7506
7592
  /** Credit balance and usage. */
7507
7593
  credits;
7508
7594
  /** Admin surface (`/api/v1/admin/*`) — requires an admin credential. */
@@ -7596,8 +7682,8 @@ var Miosa = class {
7596
7682
  this.mcp = new Mcp(this.http);
7597
7683
  this.computers = new Computers(this.http);
7598
7684
  this.sandboxes = new Sandboxes(this.http);
7685
+ this.devices = new Devices(this.http);
7599
7686
  this.deployments = new Deployments(this.http);
7600
- this.dockerDeploy = new DockerDeploy(this.http);
7601
7687
  this.credits = new Credits(this.http);
7602
7688
  this.admin = new Admin(this.http);
7603
7689
  this.openComputers = new OpenComputers(this.http);
@@ -7627,6 +7713,6 @@ var Miosa = class {
7627
7713
  }
7628
7714
  };
7629
7715
 
7630
- export { Admin, Analytics, ApiKeys, AuditLog, AuthError, Benchmarks, BuilderSessions, Channels, Checkpoints, CommandCenter, Community, Completions, Computer, ComputerAudit, ComputerAutoStop, ComputerEnv, ComputerInbox, ComputerLogs, ComputerNetwork, ComputerOsa, ComputerPorts, ComputerSecrets, ComputerTerminal, ComputerVolumes, Computers, Credits, CronJobs, Dashboard, Databases, DeploymentDomains, DeploymentReleases, DeploymentRuntimeInstances, DeploymentVersions, Deployments, Desktop, DockerDeploy, EgressAudit, EgressNetwork, EgressSecrets, Email, EmailCampaigns, EmailInbox, EmailTemplates, Embeddings, Exec, ExternalKeys, Files, FlatCustomDomains, Functions, HealthChecks, InsufficientCreditsError, Integrations, Mcp, Miosa, MiosaError, Models, NetworkError, NetworkPolicy, NotFoundError, OAuthFlow, OpenComputers, OrgInvites, ProjectAuth, ProjectIntegrations, ProviderDefaults, RateLimitError, Regions, SANDBOX_TEMPLATE, Sandbox, SandboxArtifacts, SandboxAudit, SandboxCommands, SandboxEnv, SandboxEvents, SandboxFiles, SandboxNetwork, SandboxPreview, SandboxPreviews, SandboxSecrets, SandboxTags, SandboxTemplates, SandboxTerminal, Sandboxes, ScopedFs, Settings, SnapshotsStandalone, Storage, Tenant, TimeoutError, Usage, ValidationError, Volumes, Webhooks, WorkspaceInvites, WorkspaceMembers, verifySignature };
7716
+ export { Admin, Analytics, ApiKeys, AuditLog, AuthError, Benchmarks, BuilderSessions, Channels, Checkpoints, CommandCenter, Community, Completions, Computer, ComputerAudit, ComputerAutoStop, ComputerEnv, ComputerInbox, ComputerLogs, ComputerNetwork, ComputerOsa, ComputerPorts, ComputerSecrets, ComputerTerminal, ComputerVolumes, Computers, Credits, CronJobs, Dashboard, Databases, DeploymentDomains, DeploymentReleases, DeploymentRuntimeInstances, DeploymentVersions, Deployments, Desktop, Devices, EgressAudit, EgressNetwork, EgressSecrets, Email, EmailCampaigns, EmailInbox, EmailTemplates, Embeddings, Exec, ExternalKeys, Files, FlatCustomDomains, Functions, HealthChecks, InsufficientCreditsError, Integrations, Mcp, Miosa, MiosaError, Models, NetworkError, NetworkPolicy, NotFoundError, OAuthFlow, OpenComputers, OrgInvites, ProjectAuth, ProjectIntegrations, ProviderDefaults, RateLimitError, Regions, SANDBOX_TEMPLATE, Sandbox, SandboxArtifacts, SandboxAudit, SandboxCommands, SandboxEnv, SandboxEvents, SandboxFiles, SandboxNetwork, SandboxPreview, SandboxPreviews, SandboxSecrets, SandboxTags, SandboxTemplates, SandboxTerminal, Sandboxes, ScopedFs, Settings, SnapshotsStandalone, Storage, Tenant, TimeoutError, Usage, ValidationError, Volumes, Webhooks, WorkspaceInvites, WorkspaceMembers, verifySignature };
7631
7717
  //# sourceMappingURL=index.js.map
7632
7718
  //# sourceMappingURL=index.js.map