@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miosa/sdk",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "description": "TypeScript SDK for the MIOSA API — cloud VM desktop infrastructure for AI agents",
5
5
  "license": "MIT",
6
6
  "author": "MIOSA <hello@miosa.ai>",
package/src/client.ts CHANGED
@@ -15,7 +15,7 @@ import { CronJobs } from "./resources/cron-jobs.js";
15
15
  import { Dashboard } from "./resources/dashboard.js";
16
16
  import { Databases } from "./resources/databases.js";
17
17
  import { Deployments } from "./resources/deployments.js";
18
- import { DockerDeploy } from "./resources/docker-deploy.js";
18
+ import { Devices } from "./resources/devices.js";
19
19
  import { EgressAudit } from "./resources/egressAudit.js";
20
20
  import { EgressNetwork } from "./resources/egressNetwork.js";
21
21
  import { EgressSecrets } from "./resources/egressSecrets.js";
@@ -125,15 +125,15 @@ export class Miosa {
125
125
  /** Sandboxes — native code-execution environments under `/sandboxes`. */
126
126
  readonly sandboxes: Sandboxes;
127
127
 
128
+ /** Agent device facade — route work across Sandboxes, Computers, and deploy hosts. */
129
+ readonly devices: Devices;
130
+
128
131
  /**
129
132
  * Deployments — publish from a sandbox to a stable production URL.
130
133
  * Versions, releases, rollback, custom domains.
131
134
  */
132
135
  readonly deployments: Deployments;
133
136
 
134
- /** Docker Deploy appliance hosts — one always-on workspace host, many apps. */
135
- readonly dockerDeploy: DockerDeploy;
136
-
137
137
  /** Credit balance and usage. */
138
138
  readonly credits: Credits;
139
139
 
@@ -260,8 +260,8 @@ export class Miosa {
260
260
  this.mcp = new Mcp(this.http);
261
261
  this.computers = new Computers(this.http);
262
262
  this.sandboxes = new Sandboxes(this.http);
263
+ this.devices = new Devices(this.http);
263
264
  this.deployments = new Deployments(this.http);
264
- this.dockerDeploy = new DockerDeploy(this.http);
265
265
  this.credits = new Credits(this.http);
266
266
  this.admin = new Admin(this.http);
267
267
  this.openComputers = new OpenComputers(this.http);
package/src/index.ts CHANGED
@@ -67,6 +67,16 @@ export type {
67
67
  // Resource classes (useful for isinstance checks and extension)
68
68
  export { Computer, ScopedFs } from "./resources/computer.js";
69
69
  export { Computers } from "./resources/computers.js";
70
+ export { Devices } from "./resources/devices.js";
71
+ export type {
72
+ DeviceCatalogEntry,
73
+ DeviceKind,
74
+ DeviceListError,
75
+ DeviceListParams,
76
+ DeviceListResponse,
77
+ DeviceRecord,
78
+ DeviceSource,
79
+ } from "./resources/devices.js";
70
80
  export { Desktop } from "./resources/desktop.js";
71
81
  export { Exec } from "./resources/exec.js";
72
82
  export { Files } from "./resources/files.js";
@@ -135,11 +145,17 @@ export type {
135
145
  DeploymentVersionId,
136
146
  DeploymentVersionKind,
137
147
  DeploymentVersionState,
148
+ DockerDeployDesignReferencePreset,
138
149
  DockerDeployCreateParams,
139
150
  DockerDeployDoctorCheck,
140
151
  DockerDeployDoctorParams,
141
152
  DockerDeployDoctorProbe,
142
153
  DockerDeployDoctorResult,
154
+ DockerDeployTemplateData,
155
+ DockerDeployTemplateListParams,
156
+ DockerDeployHostData,
157
+ DockerDeployHostEnsureParams,
158
+ DockerDeployHostListParams,
143
159
  ExternalAttribution,
144
160
  PublishFromSandboxParams,
145
161
  PublishParams,
@@ -151,17 +167,6 @@ export type {
151
167
  RuntimeLogsResult,
152
168
  VersionListParams,
153
169
  } from "./resources/deployments.js";
154
- export { DockerDeploy } from "./resources/docker-deploy.js";
155
- export type {
156
- DockerDeployApplianceStatus,
157
- DockerDeployHostData,
158
- DockerDeployHostEnsureParams,
159
- DockerDeployHostId,
160
- DockerDeployHostListParams,
161
- DockerDeployHostListResponse,
162
- DockerDeployHostResponse,
163
- DockerDeployHostStatus,
164
- } from "./resources/docker-deploy.js";
165
170
  export type {
166
171
  SnapshotData,
167
172
  SnapshotStatus,
@@ -345,15 +345,4 @@ export class Admin {
345
345
  model_id: modelId,
346
346
  });
347
347
  }
348
-
349
- /** POST /api/v1/admin/impersonate — returns {token, expires_at}. */
350
- impersonate(
351
- externalUserId: string,
352
- options: { ttlSec?: number } = {},
353
- ): Promise<{ token: string; expires_at: string }> {
354
- return this.http.post("/admin/impersonate", {
355
- external_user_id: externalUserId,
356
- ttl_sec: options.ttlSec ?? 3600,
357
- });
358
- }
359
348
  }
@@ -113,22 +113,6 @@ export class ApiKeys {
113
113
  return unwrap<ApiKeyCreateResult>(data);
114
114
  }
115
115
 
116
- /** POST /api/v1/api-keys/scoped — L2 delegation token bound to one external user. */
117
- async createScoped(params: {
118
- externalUserId: string;
119
- scopes: string[];
120
- expiresAt?: string;
121
- }): Promise<ApiKeyCreateResult> {
122
- const body = stripUndefined({
123
- external_user_id: params.externalUserId,
124
- scopes: params.scopes,
125
- expires_at: params.expiresAt,
126
- });
127
- return unwrap<ApiKeyCreateResult>(
128
- await this.http.post<unknown>("/api-keys/scoped", body),
129
- );
130
- }
131
-
132
116
  async delete(keyId: string): Promise<void> {
133
117
  await this.http.delete<unknown>(`/api-keys/${keyId}`);
134
118
  }
@@ -41,7 +41,7 @@ export interface CustomDomainRegisterParams {
41
41
  * // 1. Register the domain
42
42
  * const domain = await computer.domains.register("app.example.com");
43
43
  * console.log(domain.instructions);
44
- * // => "Add a CNAME record: app.example.com → <slug>.sandbox.miosa.ai"
44
+ * // => "Add a CNAME record: app.example.com → <slug>.sandbox.<tenant-domain>"
45
45
  *
46
46
  * // 2. Add the CNAME in your DNS registrar, then...
47
47
  *
@@ -14,7 +14,6 @@ function makeHttp(): HttpClient {
14
14
 
15
15
  beforeEach(() => {
16
16
  vi.clearAllMocks();
17
- vi.unstubAllGlobals();
18
17
  });
19
18
 
20
19
  describe("Deployments", () => {
@@ -56,139 +55,143 @@ describe("Deployments", () => {
56
55
  expect(deployment.docker_deploy_host_id).toBe("ddh_123");
57
56
  });
58
57
 
59
- it("doctorDockerDeploy verifies host, route metadata, and public URL", async () => {
60
- mockGet.mockImplementation(async (path: string) => {
61
- if (path === "/deployments/dep_123") {
62
- return {
63
- data: {
64
- id: "dep_123",
65
- tenant_id: "ten_123",
66
- name: "Clinic Intake",
67
- slug: "clinic-intake",
68
- state: "running",
69
- deployment_product: "docker_deploy",
70
- docker_deploy_host_id: "ddh_123",
71
- public_url: "https://clinic-intake.osa.miosa.app",
72
- metadata: {
73
- deployment_product: "docker_deploy",
74
- runtime: { ip: "172.16.74.246", port: 23906 },
75
- docker_deploy: {
76
- app_id: "miosa-clinic-intake",
77
- container_id: "container_123",
78
- status: "running",
79
- url: "http://127.0.0.1:23906",
58
+ it("lists Docker Deploy templates", async () => {
59
+ mockGet.mockResolvedValue({
60
+ data: {
61
+ templates: [
62
+ {
63
+ id: "nextjs-app",
64
+ name: "Next.js App",
65
+ runtime: "node",
66
+ framework: "nextjs",
67
+ design_md: "# Design",
68
+ design_reference_presets: [
69
+ {
70
+ id: "fintech-control",
71
+ name: "Fintech Control",
72
+ summary: "Metrics-heavy finance app.",
73
+ best_for: ["billing portals"],
80
74
  },
81
- },
75
+ ],
82
76
  },
83
- };
84
- }
85
-
86
- if (path === "/docker-deploy/hosts/ddh_123") {
87
- return {
88
- data: {
89
- id: "ddh_123",
90
- tenant_id: "ten_123",
91
- workspace_id: "ws_123",
92
- status: "active",
93
- size: "medium",
94
- region: "us",
95
- appliance_status: "healthy",
96
- },
97
- };
98
- }
77
+ ],
78
+ },
79
+ });
80
+
81
+ const templates = await new Deployments(
82
+ makeHttp(),
83
+ ).listDockerDeployTemplates({
84
+ framework: "nextjs",
85
+ includePreview: true,
86
+ });
99
87
 
100
- throw new Error(`unexpected path ${path}`);
88
+ expect(mockGet).toHaveBeenCalledWith("/docker-deploy/templates", {
89
+ framework: "nextjs",
90
+ include_preview: true,
101
91
  });
102
- vi.stubGlobal(
103
- "fetch",
104
- vi.fn().mockResolvedValue({ ok: true, status: 200 }),
92
+ expect(templates[0]?.id).toBe("nextjs-app");
93
+ expect(templates[0]?.design_reference_presets?.[0]?.id).toBe(
94
+ "fintech-control",
105
95
  );
96
+ });
106
97
 
107
- const result = await new Deployments(makeHttp()).doctorDockerDeploy(
108
- "dep_123",
109
- { probePath: "/health" },
98
+ it("gets one Docker Deploy template", async () => {
99
+ mockGet.mockResolvedValue({
100
+ data: {
101
+ id: "compose-full-stack",
102
+ name: "Compose Full Stack",
103
+ runtime: "docker-compose",
104
+ },
105
+ });
106
+
107
+ const template = await new Deployments(makeHttp()).getDockerDeployTemplate(
108
+ "compose-full-stack",
110
109
  );
111
110
 
112
- expect(result.ok).toBe(true);
113
- expect(mockGet).toHaveBeenCalledWith("/deployments/dep_123");
114
- expect(mockGet).toHaveBeenCalledWith("/docker-deploy/hosts/ddh_123");
115
- expect(fetch).toHaveBeenCalledWith(
116
- "https://clinic-intake.osa.miosa.app/health",
117
- expect.objectContaining({ method: "GET" }),
111
+ expect(mockGet).toHaveBeenCalledWith(
112
+ "/docker-deploy/templates/compose-full-stack",
118
113
  );
119
- expect(result.checks.map((check) => check.name)).toEqual([
120
- "deployment_product",
121
- "docker_deploy_host_id",
122
- "docker_deploy_host_health",
123
- "docker_deploy_app",
124
- "runtime_route",
125
- "public_url_probe",
126
- ]);
114
+ expect(template.runtime).toBe("docker-compose");
127
115
  });
128
116
 
129
- it("doctorDockerDeploy fails when route metadata does not point at the Docker container port", async () => {
130
- mockGet.mockImplementation(async (path: string) => {
131
- if (path === "/deployments/dep_123") {
132
- return {
133
- data: {
134
- id: "dep_123",
135
- tenant_id: "ten_123",
136
- name: "Clinic Intake",
137
- slug: "clinic-intake",
138
- state: "running",
117
+ it("doctorDockerDeploy() proves appliance metadata and public URL", async () => {
118
+ mockGet
119
+ .mockResolvedValueOnce({
120
+ data: {
121
+ id: "dep_123",
122
+ tenant_id: "ten_123",
123
+ name: "Clinic Intake",
124
+ slug: "clinic-intake",
125
+ state: "running",
126
+ deployment_product: "docker_deploy",
127
+ docker_deploy_host_id: "ddh_123",
128
+ public_url: "https://clinic.osa.miosa.app",
129
+ metadata: {
139
130
  deployment_product: "docker_deploy",
140
- docker_deploy_host_id: "ddh_123",
141
- public_url: "https://clinic-intake.osa.miosa.app",
142
- metadata: {
143
- deployment_product: "docker_deploy",
144
- runtime: { ip: "172.16.74.246", port: 8080 },
145
- docker_deploy: {
146
- app_id: "miosa-clinic-intake",
147
- container_id: "container_123",
148
- status: "running",
149
- url: "http://127.0.0.1:23906",
150
- },
151
- },
131
+ runtime: { ip: "172.16.74.246", port: 23906 },
152
132
  },
153
- };
154
- }
155
-
156
- if (path === "/docker-deploy/hosts/ddh_123") {
157
- return {
158
- data: {
159
- id: "ddh_123",
160
- tenant_id: "ten_123",
161
- workspace_id: "ws_123",
162
- status: "active",
163
- size: "medium",
164
- region: "us",
165
- appliance_status: "healthy",
166
- },
167
- };
168
- }
169
-
170
- throw new Error(`unexpected path ${path}`);
133
+ },
134
+ })
135
+ .mockResolvedValueOnce({
136
+ data: {
137
+ id: "ddh_123",
138
+ status: "active",
139
+ appliance_status: "healthy",
140
+ },
141
+ });
142
+ const fetchImpl = vi.fn().mockResolvedValue({
143
+ ok: true,
144
+ status: 200,
145
+ text: async () => JSON.stringify({ ok: true, product: "clinic" }),
171
146
  });
172
- vi.stubGlobal(
173
- "fetch",
174
- vi.fn().mockResolvedValue({ ok: true, status: 200 }),
147
+
148
+ const result = await new Deployments(makeHttp()).doctorDockerDeploy(
149
+ "dep_123",
150
+ { probePath: "/health", fetchImpl },
175
151
  );
176
152
 
153
+ expect(mockGet).toHaveBeenNthCalledWith(1, "/deployments/dep_123");
154
+ expect(mockGet).toHaveBeenNthCalledWith(2, "/docker-deploy/hosts/ddh_123");
155
+ expect(fetchImpl).toHaveBeenCalledWith(
156
+ "https://clinic.osa.miosa.app/health",
157
+ expect.objectContaining({ headers: { Accept: "*/*" } }),
158
+ );
159
+ expect(result.ok).toBe(true);
160
+ expect(result.host?.id).toBe("ddh_123");
161
+ expect(result.probe?.status).toBe(200);
162
+ });
163
+
164
+ it("doctorDockerDeploy() flags gateway JSON as not the user app", async () => {
165
+ mockGet
166
+ .mockResolvedValueOnce({
167
+ data: {
168
+ id: "dep_123",
169
+ tenant_id: "ten_123",
170
+ name: "Clinic Intake",
171
+ slug: "clinic-intake",
172
+ state: "running",
173
+ deployment_product: "docker_deploy",
174
+ docker_deploy_host_id: "ddh_123",
175
+ public_url: "https://clinic.osa.miosa.app",
176
+ metadata: { runtime: { ip: "172.16.74.246", port: 23906 } },
177
+ },
178
+ })
179
+ .mockResolvedValueOnce({ data: { id: "ddh_123", status: "active" } });
180
+ const fetchImpl = vi.fn().mockResolvedValue({
181
+ ok: true,
182
+ status: 200,
183
+ text: async () => JSON.stringify({ ok: true, run_id: "ciq-123" }),
184
+ });
185
+
177
186
  const result = await new Deployments(makeHttp()).doctorDockerDeploy(
178
187
  "dep_123",
179
- { probePath: "/health" },
188
+ { fetchImpl },
180
189
  );
181
190
 
182
191
  expect(result.ok).toBe(false);
183
- expect(result.checks).toEqual(
184
- expect.arrayContaining([
185
- expect.objectContaining({
186
- name: "runtime_route",
187
- ok: false,
188
- message:
189
- "Deployment route port 8080 does not match Docker container host port 23906.",
190
- }),
191
- ]),
192
- );
192
+ expect(result.probe?.gatewayJson).toBe(true);
193
+ expect(
194
+ result.checks.find((check) => check.name === "public_url_probe")?.message,
195
+ ).toContain("gateway JSON");
193
196
  });
194
197
  });