@miosa/sdk 1.2.3 → 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.3",
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,66 +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
- },
76
- },
77
- };
78
- }
79
-
80
- if (path === "/docker-deploy/hosts/ddh_123") {
81
- return {
82
- data: {
83
- id: "ddh_123",
84
- tenant_id: "ten_123",
85
- workspace_id: "ws_123",
86
- status: "active",
87
- size: "medium",
88
- region: "us",
89
- appliance_status: "healthy",
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"],
74
+ },
75
+ ],
90
76
  },
91
- };
92
- }
77
+ ],
78
+ },
79
+ });
80
+
81
+ const templates = await new Deployments(
82
+ makeHttp(),
83
+ ).listDockerDeployTemplates({
84
+ framework: "nextjs",
85
+ includePreview: true,
86
+ });
93
87
 
94
- throw new Error(`unexpected path ${path}`);
88
+ expect(mockGet).toHaveBeenCalledWith("/docker-deploy/templates", {
89
+ framework: "nextjs",
90
+ include_preview: true,
95
91
  });
96
- vi.stubGlobal(
97
- "fetch",
98
- 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",
99
95
  );
96
+ });
97
+
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",
109
+ );
110
+
111
+ expect(mockGet).toHaveBeenCalledWith(
112
+ "/docker-deploy/templates/compose-full-stack",
113
+ );
114
+ expect(template.runtime).toBe("docker-compose");
115
+ });
116
+
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: {
130
+ deployment_product: "docker_deploy",
131
+ runtime: { ip: "172.16.74.246", port: 23906 },
132
+ },
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" }),
146
+ });
100
147
 
101
148
  const result = await new Deployments(makeHttp()).doctorDockerDeploy(
102
149
  "dep_123",
103
- { probePath: "/health" },
150
+ { probePath: "/health", fetchImpl },
104
151
  );
105
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
+ );
106
159
  expect(result.ok).toBe(true);
107
- expect(mockGet).toHaveBeenCalledWith("/deployments/dep_123");
108
- expect(mockGet).toHaveBeenCalledWith("/docker-deploy/hosts/ddh_123");
109
- expect(fetch).toHaveBeenCalledWith(
110
- "https://clinic-intake.osa.miosa.app/health",
111
- expect.objectContaining({ method: "GET" }),
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
+
186
+ const result = await new Deployments(makeHttp()).doctorDockerDeploy(
187
+ "dep_123",
188
+ { fetchImpl },
112
189
  );
113
- expect(result.checks.map((check) => check.name)).toEqual([
114
- "deployment_product",
115
- "docker_deploy_host_id",
116
- "docker_deploy_host_health",
117
- "runtime_route",
118
- "public_url_probe",
119
- ]);
190
+
191
+ expect(result.ok).toBe(false);
192
+ expect(result.probe?.gatewayJson).toBe(true);
193
+ expect(
194
+ result.checks.find((check) => check.name === "public_url_probe")?.message,
195
+ ).toContain("gateway JSON");
120
196
  });
121
197
  });