@miosa/cli 1.0.7 → 1.0.8
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/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +1428 -240
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/menu.d.ts.map +1 -1
- package/dist/commands/menu.js +28 -3
- package/dist/commands/menu.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -22,6 +22,7 @@ import { request } from "undici";
|
|
|
22
22
|
import chalk from "chalk";
|
|
23
23
|
import { loadConfig } from "../config.js";
|
|
24
24
|
import { MiosaClient } from "../client.js";
|
|
25
|
+
import { banner, errorEnvelope, hintBlock, icon, kvPanel, printElapsed, formatDuration, } from "../ui/render.js";
|
|
25
26
|
// ── Tool definitions — mirror the Python MCP server exactly ─────────────────
|
|
26
27
|
const TOOL_LIST = [
|
|
27
28
|
// Lifecycle
|
|
@@ -58,6 +59,15 @@ const TOOL_LIST = [
|
|
|
58
59
|
type: "string",
|
|
59
60
|
description: "Your internal project ID for attribution (optional)",
|
|
60
61
|
},
|
|
62
|
+
gpu_model: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "GPU model to attach (e.g. 'nvidia-a10g', 'nvidia-t4'). Omit for CPU-only.",
|
|
65
|
+
},
|
|
66
|
+
gpu_count: {
|
|
67
|
+
type: "integer",
|
|
68
|
+
description: "Number of GPUs to attach (default: 1 when gpu_model is set).",
|
|
69
|
+
default: 1,
|
|
70
|
+
},
|
|
61
71
|
},
|
|
62
72
|
required: ["name"],
|
|
63
73
|
},
|
|
@@ -1307,368 +1317,971 @@ const TOOL_LIST = [
|
|
|
1307
1317
|
},
|
|
1308
1318
|
// Deployments
|
|
1309
1319
|
{
|
|
1310
|
-
name:
|
|
1311
|
-
description:
|
|
1312
|
-
inputSchema: { type:
|
|
1320
|
+
name: "deployment_list",
|
|
1321
|
+
description: "List all deployments in the tenant.",
|
|
1322
|
+
inputSchema: { type: "object", properties: {} },
|
|
1323
|
+
},
|
|
1324
|
+
{
|
|
1325
|
+
name: "deployment_get",
|
|
1326
|
+
description: "Get details of a specific deployment.",
|
|
1327
|
+
inputSchema: {
|
|
1328
|
+
type: "object",
|
|
1329
|
+
properties: {
|
|
1330
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1331
|
+
},
|
|
1332
|
+
required: ["deployment_id"],
|
|
1333
|
+
},
|
|
1313
1334
|
},
|
|
1314
1335
|
{
|
|
1315
|
-
name:
|
|
1316
|
-
description:
|
|
1336
|
+
name: "deployment_create",
|
|
1337
|
+
description: "Create a new deployment.",
|
|
1317
1338
|
inputSchema: {
|
|
1318
|
-
type:
|
|
1339
|
+
type: "object",
|
|
1319
1340
|
properties: {
|
|
1320
|
-
|
|
1341
|
+
name: { type: "string", description: "Deployment name" },
|
|
1342
|
+
type: {
|
|
1343
|
+
type: "string",
|
|
1344
|
+
description: "Deployment type (e.g. web, worker)",
|
|
1345
|
+
},
|
|
1346
|
+
source: { type: "object", description: "Source configuration" },
|
|
1347
|
+
env_vars: {
|
|
1348
|
+
type: "object",
|
|
1349
|
+
description: "Environment variables as key-value pairs",
|
|
1350
|
+
},
|
|
1351
|
+
region: { type: "string", description: "Deployment region (optional)" },
|
|
1321
1352
|
},
|
|
1322
|
-
required: [
|
|
1353
|
+
required: ["name"],
|
|
1323
1354
|
},
|
|
1324
1355
|
},
|
|
1325
1356
|
{
|
|
1326
|
-
name:
|
|
1327
|
-
description:
|
|
1357
|
+
name: "deployment_delete",
|
|
1358
|
+
description: "Delete a deployment permanently.",
|
|
1328
1359
|
inputSchema: {
|
|
1329
|
-
type:
|
|
1360
|
+
type: "object",
|
|
1330
1361
|
properties: {
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
region: { type: 'string', description: 'Deployment region (optional)' },
|
|
1362
|
+
deployment_id: {
|
|
1363
|
+
type: "string",
|
|
1364
|
+
description: "Deployment ID to delete",
|
|
1365
|
+
},
|
|
1336
1366
|
},
|
|
1337
|
-
required: [
|
|
1367
|
+
required: ["deployment_id"],
|
|
1338
1368
|
},
|
|
1339
1369
|
},
|
|
1340
1370
|
{
|
|
1341
|
-
name:
|
|
1342
|
-
description:
|
|
1371
|
+
name: "deployment_publish",
|
|
1372
|
+
description: "Publish a new version of a deployment.",
|
|
1343
1373
|
inputSchema: {
|
|
1344
|
-
type:
|
|
1374
|
+
type: "object",
|
|
1345
1375
|
properties: {
|
|
1346
|
-
deployment_id: { type:
|
|
1376
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1377
|
+
source: {
|
|
1378
|
+
type: "object",
|
|
1379
|
+
description: "Source configuration for the new version",
|
|
1380
|
+
},
|
|
1347
1381
|
},
|
|
1348
|
-
required: [
|
|
1382
|
+
required: ["deployment_id"],
|
|
1349
1383
|
},
|
|
1350
1384
|
},
|
|
1351
1385
|
{
|
|
1352
|
-
name:
|
|
1353
|
-
description:
|
|
1386
|
+
name: "deployment_rollback",
|
|
1387
|
+
description: "Rollback a deployment to a previous version.",
|
|
1354
1388
|
inputSchema: {
|
|
1355
|
-
type:
|
|
1389
|
+
type: "object",
|
|
1356
1390
|
properties: {
|
|
1357
|
-
deployment_id: { type:
|
|
1358
|
-
|
|
1391
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1392
|
+
version_id: {
|
|
1393
|
+
type: "string",
|
|
1394
|
+
description: "Version ID to roll back to",
|
|
1395
|
+
},
|
|
1359
1396
|
},
|
|
1360
|
-
required: [
|
|
1397
|
+
required: ["deployment_id", "version_id"],
|
|
1361
1398
|
},
|
|
1362
1399
|
},
|
|
1363
1400
|
{
|
|
1364
|
-
name:
|
|
1365
|
-
description:
|
|
1401
|
+
name: "deployment_env_list",
|
|
1402
|
+
description: "List environment variables for a deployment.",
|
|
1366
1403
|
inputSchema: {
|
|
1367
|
-
type:
|
|
1404
|
+
type: "object",
|
|
1368
1405
|
properties: {
|
|
1369
|
-
deployment_id: { type:
|
|
1370
|
-
version_id: { type: 'string', description: 'Version ID to roll back to' },
|
|
1406
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1371
1407
|
},
|
|
1372
|
-
required: [
|
|
1408
|
+
required: ["deployment_id"],
|
|
1373
1409
|
},
|
|
1374
1410
|
},
|
|
1375
1411
|
{
|
|
1376
|
-
name:
|
|
1377
|
-
description:
|
|
1412
|
+
name: "deployment_env_set",
|
|
1413
|
+
description: "Set (create or update) an environment variable for a deployment.",
|
|
1378
1414
|
inputSchema: {
|
|
1379
|
-
type:
|
|
1415
|
+
type: "object",
|
|
1380
1416
|
properties: {
|
|
1381
|
-
deployment_id: { type:
|
|
1417
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1418
|
+
key: { type: "string", description: "Environment variable name" },
|
|
1419
|
+
value: { type: "string", description: "Environment variable value" },
|
|
1382
1420
|
},
|
|
1383
|
-
required: [
|
|
1421
|
+
required: ["deployment_id", "key", "value"],
|
|
1384
1422
|
},
|
|
1385
1423
|
},
|
|
1386
1424
|
{
|
|
1387
|
-
name:
|
|
1388
|
-
description:
|
|
1425
|
+
name: "deployment_logs",
|
|
1426
|
+
description: "Get logs for a deployment.",
|
|
1389
1427
|
inputSchema: {
|
|
1390
|
-
type:
|
|
1428
|
+
type: "object",
|
|
1391
1429
|
properties: {
|
|
1392
|
-
deployment_id: { type:
|
|
1393
|
-
|
|
1394
|
-
|
|
1430
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1431
|
+
lines: {
|
|
1432
|
+
type: "integer",
|
|
1433
|
+
description: "Number of log lines to return (default: 100)",
|
|
1434
|
+
default: 100,
|
|
1435
|
+
},
|
|
1436
|
+
since: {
|
|
1437
|
+
type: "string",
|
|
1438
|
+
description: "ISO 8601 timestamp to fetch logs from (optional)",
|
|
1439
|
+
},
|
|
1395
1440
|
},
|
|
1396
|
-
required: [
|
|
1441
|
+
required: ["deployment_id"],
|
|
1397
1442
|
},
|
|
1398
1443
|
},
|
|
1399
1444
|
{
|
|
1400
|
-
name:
|
|
1401
|
-
description:
|
|
1445
|
+
name: "deployment_version_list",
|
|
1446
|
+
description: "List all versions of a deployment.",
|
|
1402
1447
|
inputSchema: {
|
|
1403
|
-
type:
|
|
1448
|
+
type: "object",
|
|
1404
1449
|
properties: {
|
|
1405
|
-
deployment_id: { type:
|
|
1406
|
-
|
|
1407
|
-
|
|
1450
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1451
|
+
},
|
|
1452
|
+
required: ["deployment_id"],
|
|
1453
|
+
},
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
name: "deployment_version_promote",
|
|
1457
|
+
description: "Promote a specific version to be the active deployment.",
|
|
1458
|
+
inputSchema: {
|
|
1459
|
+
type: "object",
|
|
1460
|
+
properties: {
|
|
1461
|
+
deployment_id: { type: "string", description: "Deployment ID" },
|
|
1462
|
+
version_id: { type: "string", description: "Version ID to promote" },
|
|
1463
|
+
},
|
|
1464
|
+
required: ["deployment_id", "version_id"],
|
|
1465
|
+
},
|
|
1466
|
+
},
|
|
1467
|
+
// Storage
|
|
1468
|
+
{
|
|
1469
|
+
name: "storage_bucket_list",
|
|
1470
|
+
description: "List all storage buckets in the tenant.",
|
|
1471
|
+
inputSchema: { type: "object", properties: {} },
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
name: "storage_bucket_create",
|
|
1475
|
+
description: "Create a new storage bucket.",
|
|
1476
|
+
inputSchema: {
|
|
1477
|
+
type: "object",
|
|
1478
|
+
properties: {
|
|
1479
|
+
name: { type: "string", description: "Bucket name" },
|
|
1480
|
+
region: { type: "string", description: "Bucket region (optional)" },
|
|
1481
|
+
public: {
|
|
1482
|
+
type: "boolean",
|
|
1483
|
+
description: "Whether the bucket is publicly readable (default: false)",
|
|
1484
|
+
default: false,
|
|
1485
|
+
},
|
|
1486
|
+
},
|
|
1487
|
+
required: ["name"],
|
|
1488
|
+
},
|
|
1489
|
+
},
|
|
1490
|
+
{
|
|
1491
|
+
name: "storage_bucket_delete",
|
|
1492
|
+
description: "Delete a storage bucket.",
|
|
1493
|
+
inputSchema: {
|
|
1494
|
+
type: "object",
|
|
1495
|
+
properties: {
|
|
1496
|
+
bucket_id: {
|
|
1497
|
+
type: "string",
|
|
1498
|
+
description: "Bucket ID or name to delete",
|
|
1499
|
+
},
|
|
1500
|
+
},
|
|
1501
|
+
required: ["bucket_id"],
|
|
1502
|
+
},
|
|
1503
|
+
},
|
|
1504
|
+
{
|
|
1505
|
+
name: "storage_object_list",
|
|
1506
|
+
description: "List objects in a storage bucket, optionally filtered by prefix.",
|
|
1507
|
+
inputSchema: {
|
|
1508
|
+
type: "object",
|
|
1509
|
+
properties: {
|
|
1510
|
+
bucket_id: { type: "string", description: "Bucket ID or name" },
|
|
1511
|
+
prefix: {
|
|
1512
|
+
type: "string",
|
|
1513
|
+
description: "Key prefix to filter by (optional)",
|
|
1514
|
+
},
|
|
1515
|
+
},
|
|
1516
|
+
required: ["bucket_id"],
|
|
1517
|
+
},
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
name: "storage_object_upload",
|
|
1521
|
+
description: "Upload an object to a storage bucket.",
|
|
1522
|
+
inputSchema: {
|
|
1523
|
+
type: "object",
|
|
1524
|
+
properties: {
|
|
1525
|
+
bucket_id: { type: "string", description: "Bucket ID or name" },
|
|
1526
|
+
key: { type: "string", description: "Object key (path within bucket)" },
|
|
1527
|
+
content: {
|
|
1528
|
+
type: "string",
|
|
1529
|
+
description: "Object content (text or base64-encoded binary)",
|
|
1530
|
+
},
|
|
1531
|
+
content_type: {
|
|
1532
|
+
type: "string",
|
|
1533
|
+
description: "MIME type of the object (optional)",
|
|
1534
|
+
},
|
|
1535
|
+
},
|
|
1536
|
+
required: ["bucket_id", "key", "content"],
|
|
1537
|
+
},
|
|
1538
|
+
},
|
|
1539
|
+
{
|
|
1540
|
+
name: "storage_object_download",
|
|
1541
|
+
description: "Download an object from a storage bucket.",
|
|
1542
|
+
inputSchema: {
|
|
1543
|
+
type: "object",
|
|
1544
|
+
properties: {
|
|
1545
|
+
bucket_id: { type: "string", description: "Bucket ID or name" },
|
|
1546
|
+
key: { type: "string", description: "Object key to download" },
|
|
1547
|
+
},
|
|
1548
|
+
required: ["bucket_id", "key"],
|
|
1549
|
+
},
|
|
1550
|
+
},
|
|
1551
|
+
{
|
|
1552
|
+
name: "storage_object_delete",
|
|
1553
|
+
description: "Delete an object from a storage bucket.",
|
|
1554
|
+
inputSchema: {
|
|
1555
|
+
type: "object",
|
|
1556
|
+
properties: {
|
|
1557
|
+
bucket_id: { type: "string", description: "Bucket ID or name" },
|
|
1558
|
+
key: { type: "string", description: "Object key to delete" },
|
|
1559
|
+
},
|
|
1560
|
+
required: ["bucket_id", "key"],
|
|
1561
|
+
},
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
name: "storage_presign",
|
|
1565
|
+
description: "Get a presigned URL for temporary access to a storage object.",
|
|
1566
|
+
inputSchema: {
|
|
1567
|
+
type: "object",
|
|
1568
|
+
properties: {
|
|
1569
|
+
bucket_id: { type: "string", description: "Bucket ID or name" },
|
|
1570
|
+
key: { type: "string", description: "Object key" },
|
|
1571
|
+
expires_in: {
|
|
1572
|
+
type: "integer",
|
|
1573
|
+
description: "URL expiry in seconds (default: 3600)",
|
|
1574
|
+
default: 3600,
|
|
1575
|
+
},
|
|
1576
|
+
method: {
|
|
1577
|
+
type: "string",
|
|
1578
|
+
enum: ["GET", "PUT"],
|
|
1579
|
+
description: "HTTP method (default: GET)",
|
|
1580
|
+
default: "GET",
|
|
1581
|
+
},
|
|
1582
|
+
},
|
|
1583
|
+
required: ["bucket_id", "key"],
|
|
1584
|
+
},
|
|
1585
|
+
},
|
|
1586
|
+
// Databases
|
|
1587
|
+
{
|
|
1588
|
+
name: "database_list",
|
|
1589
|
+
description: "List all managed databases in the tenant.",
|
|
1590
|
+
inputSchema: { type: "object", properties: {} },
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
name: "database_create",
|
|
1594
|
+
description: "Create a new managed database.",
|
|
1595
|
+
inputSchema: {
|
|
1596
|
+
type: "object",
|
|
1597
|
+
properties: {
|
|
1598
|
+
name: { type: "string", description: "Database name" },
|
|
1599
|
+
engine: {
|
|
1600
|
+
type: "string",
|
|
1601
|
+
enum: ["postgres", "mysql", "redis"],
|
|
1602
|
+
description: "Database engine",
|
|
1603
|
+
},
|
|
1604
|
+
version: { type: "string", description: "Engine version (optional)" },
|
|
1605
|
+
size: { type: "string", description: "Database size/tier (optional)" },
|
|
1606
|
+
region: { type: "string", description: "Region (optional)" },
|
|
1607
|
+
},
|
|
1608
|
+
required: ["name", "engine"],
|
|
1609
|
+
},
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
name: "database_get",
|
|
1613
|
+
description: "Get details of a specific database.",
|
|
1614
|
+
inputSchema: {
|
|
1615
|
+
type: "object",
|
|
1616
|
+
properties: {
|
|
1617
|
+
database_id: { type: "string", description: "Database ID" },
|
|
1618
|
+
},
|
|
1619
|
+
required: ["database_id"],
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
name: "database_delete",
|
|
1624
|
+
description: "Delete a managed database permanently.",
|
|
1625
|
+
inputSchema: {
|
|
1626
|
+
type: "object",
|
|
1627
|
+
properties: {
|
|
1628
|
+
database_id: { type: "string", description: "Database ID to delete" },
|
|
1629
|
+
},
|
|
1630
|
+
required: ["database_id"],
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
name: "database_credentials",
|
|
1635
|
+
description: "Get the connection string and credentials for a database.",
|
|
1636
|
+
inputSchema: {
|
|
1637
|
+
type: "object",
|
|
1638
|
+
properties: {
|
|
1639
|
+
database_id: { type: "string", description: "Database ID" },
|
|
1640
|
+
},
|
|
1641
|
+
required: ["database_id"],
|
|
1642
|
+
},
|
|
1643
|
+
},
|
|
1644
|
+
{
|
|
1645
|
+
name: "database_logs",
|
|
1646
|
+
description: "Get logs for a managed database.",
|
|
1647
|
+
inputSchema: {
|
|
1648
|
+
type: "object",
|
|
1649
|
+
properties: {
|
|
1650
|
+
database_id: { type: "string", description: "Database ID" },
|
|
1651
|
+
lines: {
|
|
1652
|
+
type: "integer",
|
|
1653
|
+
description: "Number of log lines to return (default: 100)",
|
|
1654
|
+
default: 100,
|
|
1655
|
+
},
|
|
1656
|
+
since: {
|
|
1657
|
+
type: "string",
|
|
1658
|
+
description: "ISO 8601 timestamp to fetch logs from (optional)",
|
|
1659
|
+
},
|
|
1660
|
+
},
|
|
1661
|
+
required: ["database_id"],
|
|
1662
|
+
},
|
|
1663
|
+
},
|
|
1664
|
+
// Workspaces
|
|
1665
|
+
{
|
|
1666
|
+
name: "workspace_list",
|
|
1667
|
+
description: "List all workspaces in the tenant.",
|
|
1668
|
+
inputSchema: { type: "object", properties: {} },
|
|
1669
|
+
},
|
|
1670
|
+
{
|
|
1671
|
+
name: "workspace_create",
|
|
1672
|
+
description: "Create a new workspace.",
|
|
1673
|
+
inputSchema: {
|
|
1674
|
+
type: "object",
|
|
1675
|
+
properties: {
|
|
1676
|
+
name: { type: "string", description: "Workspace name" },
|
|
1677
|
+
description: {
|
|
1678
|
+
type: "string",
|
|
1679
|
+
description: "Workspace description (optional)",
|
|
1680
|
+
},
|
|
1681
|
+
},
|
|
1682
|
+
required: ["name"],
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
name: "workspace_get",
|
|
1687
|
+
description: "Get details of a specific workspace.",
|
|
1688
|
+
inputSchema: {
|
|
1689
|
+
type: "object",
|
|
1690
|
+
properties: {
|
|
1691
|
+
workspace_id: { type: "string", description: "Workspace ID" },
|
|
1692
|
+
},
|
|
1693
|
+
required: ["workspace_id"],
|
|
1694
|
+
},
|
|
1695
|
+
},
|
|
1696
|
+
{
|
|
1697
|
+
name: "workspace_update",
|
|
1698
|
+
description: "Update a workspace's name or description.",
|
|
1699
|
+
inputSchema: {
|
|
1700
|
+
type: "object",
|
|
1701
|
+
properties: {
|
|
1702
|
+
workspace_id: { type: "string", description: "Workspace ID" },
|
|
1703
|
+
name: { type: "string", description: "New workspace name (optional)" },
|
|
1704
|
+
description: {
|
|
1705
|
+
type: "string",
|
|
1706
|
+
description: "New description (optional)",
|
|
1707
|
+
},
|
|
1708
|
+
},
|
|
1709
|
+
required: ["workspace_id"],
|
|
1710
|
+
},
|
|
1711
|
+
},
|
|
1712
|
+
{
|
|
1713
|
+
name: "workspace_stats",
|
|
1714
|
+
description: "Get resource statistics for a workspace (computers, sandboxes, databases, etc.).",
|
|
1715
|
+
inputSchema: {
|
|
1716
|
+
type: "object",
|
|
1717
|
+
properties: {
|
|
1718
|
+
workspace_id: { type: "string", description: "Workspace ID" },
|
|
1719
|
+
},
|
|
1720
|
+
required: ["workspace_id"],
|
|
1721
|
+
},
|
|
1722
|
+
},
|
|
1723
|
+
{
|
|
1724
|
+
name: "workspace_usage",
|
|
1725
|
+
description: "Get usage data (compute hours, storage, bandwidth) for a workspace.",
|
|
1726
|
+
inputSchema: {
|
|
1727
|
+
type: "object",
|
|
1728
|
+
properties: {
|
|
1729
|
+
workspace_id: { type: "string", description: "Workspace ID" },
|
|
1730
|
+
period: {
|
|
1731
|
+
type: "string",
|
|
1732
|
+
description: "Billing period (e.g. '2026-05'). Defaults to current month.",
|
|
1733
|
+
},
|
|
1734
|
+
},
|
|
1735
|
+
required: ["workspace_id"],
|
|
1736
|
+
},
|
|
1737
|
+
},
|
|
1738
|
+
// Billing
|
|
1739
|
+
{
|
|
1740
|
+
name: "billing_usage",
|
|
1741
|
+
description: "Get current billing period usage for the tenant.",
|
|
1742
|
+
inputSchema: { type: "object", properties: {} },
|
|
1743
|
+
},
|
|
1744
|
+
{
|
|
1745
|
+
name: "billing_plan",
|
|
1746
|
+
description: "Get the current billing plan details for the tenant.",
|
|
1747
|
+
inputSchema: { type: "object", properties: {} },
|
|
1748
|
+
},
|
|
1749
|
+
// Tunnels / Port forwarding
|
|
1750
|
+
{
|
|
1751
|
+
name: "computer_expose_port",
|
|
1752
|
+
description: "Expose a port on the computer and return the public URL. The URL follows the pattern https://{port}-{slug}.computer.miosa.ai.",
|
|
1753
|
+
inputSchema: {
|
|
1754
|
+
type: "object",
|
|
1755
|
+
properties: {
|
|
1756
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1757
|
+
port: { type: "integer", description: "Port number to expose" },
|
|
1758
|
+
protocol: {
|
|
1759
|
+
type: "string",
|
|
1760
|
+
enum: ["http", "https", "tcp"],
|
|
1761
|
+
description: "Protocol (default: http)",
|
|
1762
|
+
},
|
|
1763
|
+
},
|
|
1764
|
+
required: ["computer_id", "port"],
|
|
1765
|
+
},
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
name: "computer_list_ports",
|
|
1769
|
+
description: "List all currently exposed ports on the computer.",
|
|
1770
|
+
inputSchema: {
|
|
1771
|
+
type: "object",
|
|
1772
|
+
properties: {
|
|
1773
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1774
|
+
},
|
|
1775
|
+
required: ["computer_id"],
|
|
1776
|
+
},
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
name: "computer_preview_url",
|
|
1780
|
+
description: "Return the public preview URL for a given port on the computer. Format: https://{port}-{slug}.computer.miosa.ai",
|
|
1781
|
+
inputSchema: {
|
|
1782
|
+
type: "object",
|
|
1783
|
+
properties: {
|
|
1784
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1785
|
+
port: { type: "integer", description: "Port number" },
|
|
1786
|
+
},
|
|
1787
|
+
required: ["computer_id", "port"],
|
|
1788
|
+
},
|
|
1789
|
+
},
|
|
1790
|
+
// Network policy
|
|
1791
|
+
{
|
|
1792
|
+
name: "computer_network_policy_get",
|
|
1793
|
+
description: "Get the current network policy (firewall rules) for the computer.",
|
|
1794
|
+
inputSchema: {
|
|
1795
|
+
type: "object",
|
|
1796
|
+
properties: {
|
|
1797
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1798
|
+
},
|
|
1799
|
+
required: ["computer_id"],
|
|
1800
|
+
},
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
name: "computer_network_policy_set",
|
|
1804
|
+
description: "Set the network policy (firewall rules) for the computer.",
|
|
1805
|
+
inputSchema: {
|
|
1806
|
+
type: "object",
|
|
1807
|
+
properties: {
|
|
1808
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1809
|
+
rules: {
|
|
1810
|
+
type: "array",
|
|
1811
|
+
items: { type: "object" },
|
|
1812
|
+
description: "List of firewall rule objects (e.g. {direction, protocol, port, action})",
|
|
1813
|
+
},
|
|
1814
|
+
default_effect: {
|
|
1815
|
+
type: "string",
|
|
1816
|
+
enum: ["allow", "deny"],
|
|
1817
|
+
description: "Default action when no rule matches (default: allow)",
|
|
1818
|
+
},
|
|
1819
|
+
},
|
|
1820
|
+
required: ["computer_id", "rules"],
|
|
1821
|
+
},
|
|
1822
|
+
},
|
|
1823
|
+
{
|
|
1824
|
+
name: "computer_network_policy_reset",
|
|
1825
|
+
description: "Reset the network policy for the computer to the platform default (allow all).",
|
|
1826
|
+
inputSchema: {
|
|
1827
|
+
type: "object",
|
|
1828
|
+
properties: {
|
|
1829
|
+
computer_id: { type: "string", description: "Computer ID." },
|
|
1830
|
+
},
|
|
1831
|
+
required: ["computer_id"],
|
|
1832
|
+
},
|
|
1833
|
+
},
|
|
1834
|
+
// Webhooks
|
|
1835
|
+
{
|
|
1836
|
+
name: "webhook_list",
|
|
1837
|
+
description: "List all webhooks registered in the tenant.",
|
|
1838
|
+
inputSchema: { type: "object", properties: {} },
|
|
1839
|
+
},
|
|
1840
|
+
{
|
|
1841
|
+
name: "webhook_create",
|
|
1842
|
+
description: "Create a new webhook endpoint.",
|
|
1843
|
+
inputSchema: {
|
|
1844
|
+
type: "object",
|
|
1845
|
+
properties: {
|
|
1846
|
+
url: {
|
|
1847
|
+
type: "string",
|
|
1848
|
+
description: "HTTPS URL to deliver webhook events to",
|
|
1849
|
+
},
|
|
1850
|
+
events: {
|
|
1851
|
+
type: "array",
|
|
1852
|
+
items: { type: "string" },
|
|
1853
|
+
description: "List of event types to subscribe to (e.g. ['computer.started', 'computer.stopped'])",
|
|
1854
|
+
},
|
|
1855
|
+
},
|
|
1856
|
+
required: ["url", "events"],
|
|
1857
|
+
},
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
name: "webhook_delete",
|
|
1861
|
+
description: "Delete a webhook.",
|
|
1862
|
+
inputSchema: {
|
|
1863
|
+
type: "object",
|
|
1864
|
+
properties: {
|
|
1865
|
+
webhook_id: { type: "string", description: "Webhook ID to delete" },
|
|
1866
|
+
},
|
|
1867
|
+
required: ["webhook_id"],
|
|
1868
|
+
},
|
|
1869
|
+
},
|
|
1870
|
+
{
|
|
1871
|
+
name: "webhook_test",
|
|
1872
|
+
description: "Send a test event delivery to a webhook endpoint.",
|
|
1873
|
+
inputSchema: {
|
|
1874
|
+
type: "object",
|
|
1875
|
+
properties: {
|
|
1876
|
+
webhook_id: { type: "string", description: "Webhook ID to test" },
|
|
1877
|
+
},
|
|
1878
|
+
required: ["webhook_id"],
|
|
1879
|
+
},
|
|
1880
|
+
},
|
|
1881
|
+
// Functions
|
|
1882
|
+
{
|
|
1883
|
+
name: "function_list",
|
|
1884
|
+
description: "List all serverless functions in the tenant.",
|
|
1885
|
+
inputSchema: { type: "object", properties: {} },
|
|
1886
|
+
},
|
|
1887
|
+
{
|
|
1888
|
+
name: "function_create",
|
|
1889
|
+
description: "Create a new serverless function.",
|
|
1890
|
+
inputSchema: {
|
|
1891
|
+
type: "object",
|
|
1892
|
+
properties: {
|
|
1893
|
+
name: { type: "string", description: "Function name" },
|
|
1894
|
+
runtime: {
|
|
1895
|
+
type: "string",
|
|
1896
|
+
description: "Runtime identifier (e.g. 'node20', 'python311', 'go122')",
|
|
1897
|
+
},
|
|
1898
|
+
code: {
|
|
1899
|
+
type: "string",
|
|
1900
|
+
description: "Inline function source code (optional)",
|
|
1901
|
+
},
|
|
1902
|
+
},
|
|
1903
|
+
required: ["name", "runtime"],
|
|
1904
|
+
},
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
name: "function_invoke",
|
|
1908
|
+
description: "Invoke a serverless function and return its response.",
|
|
1909
|
+
inputSchema: {
|
|
1910
|
+
type: "object",
|
|
1911
|
+
properties: {
|
|
1912
|
+
function_id: { type: "string", description: "Function ID to invoke" },
|
|
1913
|
+
payload: {
|
|
1914
|
+
type: "object",
|
|
1915
|
+
description: "JSON payload to pass to the function (optional)",
|
|
1916
|
+
},
|
|
1917
|
+
},
|
|
1918
|
+
required: ["function_id"],
|
|
1919
|
+
},
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
name: "function_delete",
|
|
1923
|
+
description: "Delete a serverless function permanently.",
|
|
1924
|
+
inputSchema: {
|
|
1925
|
+
type: "object",
|
|
1926
|
+
properties: {
|
|
1927
|
+
function_id: { type: "string", description: "Function ID to delete" },
|
|
1928
|
+
},
|
|
1929
|
+
required: ["function_id"],
|
|
1930
|
+
},
|
|
1931
|
+
},
|
|
1932
|
+
// API Keys
|
|
1933
|
+
{
|
|
1934
|
+
name: "api_key_list",
|
|
1935
|
+
description: "List all API keys for the tenant.",
|
|
1936
|
+
inputSchema: { type: "object", properties: {} },
|
|
1937
|
+
},
|
|
1938
|
+
{
|
|
1939
|
+
name: "api_key_create",
|
|
1940
|
+
description: "Create a new API key.",
|
|
1941
|
+
inputSchema: {
|
|
1942
|
+
type: "object",
|
|
1943
|
+
properties: {
|
|
1944
|
+
name: {
|
|
1945
|
+
type: "string",
|
|
1946
|
+
description: "Human-readable label for the key",
|
|
1947
|
+
},
|
|
1948
|
+
scopes: {
|
|
1949
|
+
type: "array",
|
|
1950
|
+
items: { type: "string" },
|
|
1951
|
+
description: "Permission scopes for the key (optional; defaults to full access)",
|
|
1952
|
+
},
|
|
1953
|
+
},
|
|
1954
|
+
required: ["name"],
|
|
1955
|
+
},
|
|
1956
|
+
},
|
|
1957
|
+
{
|
|
1958
|
+
name: "api_key_delete",
|
|
1959
|
+
description: "Revoke and delete an API key.",
|
|
1960
|
+
inputSchema: {
|
|
1961
|
+
type: "object",
|
|
1962
|
+
properties: {
|
|
1963
|
+
key_id: { type: "string", description: "API key ID to delete" },
|
|
1964
|
+
},
|
|
1965
|
+
required: ["key_id"],
|
|
1966
|
+
},
|
|
1967
|
+
},
|
|
1968
|
+
// Regions
|
|
1969
|
+
{
|
|
1970
|
+
name: "region_list",
|
|
1971
|
+
description: "List available regions and their GPU availability.",
|
|
1972
|
+
inputSchema: { type: "object", properties: {} },
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
name: "computer_list_regions",
|
|
1976
|
+
description: "List available compute regions with GPU availability details.",
|
|
1977
|
+
inputSchema: { type: "object", properties: {} },
|
|
1978
|
+
},
|
|
1979
|
+
// Computer templates
|
|
1980
|
+
{
|
|
1981
|
+
name: "computer_template_list",
|
|
1982
|
+
description: "List computer templates available in a workspace.",
|
|
1983
|
+
inputSchema: {
|
|
1984
|
+
type: "object",
|
|
1985
|
+
properties: {
|
|
1986
|
+
workspace_id: {
|
|
1987
|
+
type: "string",
|
|
1988
|
+
description: "Workspace ID to list templates for",
|
|
1989
|
+
},
|
|
1408
1990
|
},
|
|
1409
|
-
required: [
|
|
1991
|
+
required: ["workspace_id"],
|
|
1410
1992
|
},
|
|
1411
1993
|
},
|
|
1412
1994
|
{
|
|
1413
|
-
name:
|
|
1414
|
-
description:
|
|
1995
|
+
name: "computer_template_create",
|
|
1996
|
+
description: "Create a new computer template in a workspace.",
|
|
1415
1997
|
inputSchema: {
|
|
1416
|
-
type:
|
|
1998
|
+
type: "object",
|
|
1417
1999
|
properties: {
|
|
1418
|
-
|
|
2000
|
+
workspace_id: {
|
|
2001
|
+
type: "string",
|
|
2002
|
+
description: "Workspace ID to create the template in",
|
|
2003
|
+
},
|
|
2004
|
+
name: {
|
|
2005
|
+
type: "string",
|
|
2006
|
+
description: "Human-readable name for the template",
|
|
2007
|
+
},
|
|
2008
|
+
template_type: {
|
|
2009
|
+
type: "string",
|
|
2010
|
+
description: "Base template type (e.g. miosa-desktop)",
|
|
2011
|
+
},
|
|
2012
|
+
size: {
|
|
2013
|
+
type: "string",
|
|
2014
|
+
enum: ["small", "medium", "large", "xl"],
|
|
2015
|
+
description: "VM size for the template",
|
|
2016
|
+
},
|
|
2017
|
+
selected_apps: {
|
|
2018
|
+
type: "array",
|
|
2019
|
+
items: { type: "string" },
|
|
2020
|
+
description: "List of app identifiers to pre-install",
|
|
2021
|
+
},
|
|
2022
|
+
settings: {
|
|
2023
|
+
type: "object",
|
|
2024
|
+
description: "Additional template settings (key-value pairs)",
|
|
2025
|
+
},
|
|
1419
2026
|
},
|
|
1420
|
-
required: [
|
|
2027
|
+
required: ["workspace_id", "name"],
|
|
1421
2028
|
},
|
|
1422
2029
|
},
|
|
2030
|
+
// Settings
|
|
1423
2031
|
{
|
|
1424
|
-
name:
|
|
1425
|
-
description:
|
|
1426
|
-
inputSchema: {
|
|
1427
|
-
type: 'object',
|
|
1428
|
-
properties: {
|
|
1429
|
-
deployment_id: { type: 'string', description: 'Deployment ID' },
|
|
1430
|
-
version_id: { type: 'string', description: 'Version ID to promote' },
|
|
1431
|
-
},
|
|
1432
|
-
required: ['deployment_id', 'version_id'],
|
|
1433
|
-
},
|
|
2032
|
+
name: "settings_get",
|
|
2033
|
+
description: "Get all tenant-level settings.",
|
|
2034
|
+
inputSchema: { type: "object", properties: {} },
|
|
1434
2035
|
},
|
|
1435
|
-
// Storage
|
|
1436
2036
|
{
|
|
1437
|
-
name:
|
|
1438
|
-
description:
|
|
1439
|
-
inputSchema: { type:
|
|
2037
|
+
name: "settings_get_branding",
|
|
2038
|
+
description: "Get tenant branding settings (logo, wallpaper, colours).",
|
|
2039
|
+
inputSchema: { type: "object", properties: {} },
|
|
1440
2040
|
},
|
|
1441
2041
|
{
|
|
1442
|
-
name:
|
|
1443
|
-
description:
|
|
2042
|
+
name: "settings_update_branding",
|
|
2043
|
+
description: "Update tenant branding settings.",
|
|
1444
2044
|
inputSchema: {
|
|
1445
|
-
type:
|
|
2045
|
+
type: "object",
|
|
1446
2046
|
properties: {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
2047
|
+
desktop_wallpaper_url: {
|
|
2048
|
+
type: "string",
|
|
2049
|
+
description: "HTTPS URL for the default desktop wallpaper (optional)",
|
|
2050
|
+
},
|
|
2051
|
+
logo_url: {
|
|
2052
|
+
type: "string",
|
|
2053
|
+
description: "HTTPS URL for the tenant logo (optional)",
|
|
2054
|
+
},
|
|
1450
2055
|
},
|
|
1451
|
-
required: ['name'],
|
|
1452
2056
|
},
|
|
1453
2057
|
},
|
|
1454
2058
|
{
|
|
1455
|
-
name:
|
|
1456
|
-
description:
|
|
1457
|
-
inputSchema: {
|
|
1458
|
-
type: 'object',
|
|
1459
|
-
properties: {
|
|
1460
|
-
bucket_id: { type: 'string', description: 'Bucket ID or name to delete' },
|
|
1461
|
-
},
|
|
1462
|
-
required: ['bucket_id'],
|
|
1463
|
-
},
|
|
2059
|
+
name: "settings_compute_pricing",
|
|
2060
|
+
description: "Get compute pricing information for available sizes and GPU types.",
|
|
2061
|
+
inputSchema: { type: "object", properties: {} },
|
|
1464
2062
|
},
|
|
2063
|
+
// Sandbox template extensions
|
|
1465
2064
|
{
|
|
1466
|
-
name:
|
|
1467
|
-
description:
|
|
2065
|
+
name: "sandbox_template_get",
|
|
2066
|
+
description: "Get details of a specific sandbox template.",
|
|
1468
2067
|
inputSchema: {
|
|
1469
|
-
type:
|
|
2068
|
+
type: "object",
|
|
1470
2069
|
properties: {
|
|
1471
|
-
|
|
1472
|
-
|
|
2070
|
+
template_id: {
|
|
2071
|
+
type: "string",
|
|
2072
|
+
description: "Sandbox template ID or slug",
|
|
2073
|
+
},
|
|
1473
2074
|
},
|
|
1474
|
-
required: [
|
|
2075
|
+
required: ["template_id"],
|
|
1475
2076
|
},
|
|
1476
2077
|
},
|
|
1477
2078
|
{
|
|
1478
|
-
name:
|
|
1479
|
-
description:
|
|
2079
|
+
name: "sandbox_template_builds",
|
|
2080
|
+
description: "List builds for a sandbox template.",
|
|
1480
2081
|
inputSchema: {
|
|
1481
|
-
type:
|
|
2082
|
+
type: "object",
|
|
1482
2083
|
properties: {
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
2084
|
+
template_id: {
|
|
2085
|
+
type: "string",
|
|
2086
|
+
description: "Sandbox template ID or slug",
|
|
2087
|
+
},
|
|
1487
2088
|
},
|
|
1488
|
-
required: [
|
|
2089
|
+
required: ["template_id"],
|
|
1489
2090
|
},
|
|
1490
2091
|
},
|
|
2092
|
+
// Cron jobs
|
|
1491
2093
|
{
|
|
1492
|
-
name:
|
|
1493
|
-
description:
|
|
2094
|
+
name: "cron_list",
|
|
2095
|
+
description: "List all cron jobs in the tenant, optionally filtered by computer.",
|
|
1494
2096
|
inputSchema: {
|
|
1495
|
-
type:
|
|
2097
|
+
type: "object",
|
|
1496
2098
|
properties: {
|
|
1497
|
-
|
|
1498
|
-
|
|
2099
|
+
computer_id: {
|
|
2100
|
+
type: "string",
|
|
2101
|
+
description: "Filter cron jobs by computer ID (optional)",
|
|
2102
|
+
},
|
|
1499
2103
|
},
|
|
1500
|
-
required: ['bucket_id', 'key'],
|
|
1501
2104
|
},
|
|
1502
2105
|
},
|
|
1503
2106
|
{
|
|
1504
|
-
name:
|
|
1505
|
-
description:
|
|
2107
|
+
name: "cron_create",
|
|
2108
|
+
description: "Create a new cron job that runs a command on a schedule.",
|
|
1506
2109
|
inputSchema: {
|
|
1507
|
-
type:
|
|
2110
|
+
type: "object",
|
|
1508
2111
|
properties: {
|
|
1509
|
-
|
|
1510
|
-
|
|
2112
|
+
computer_id: {
|
|
2113
|
+
type: "string",
|
|
2114
|
+
description: "ID of the computer to run the cron job on",
|
|
2115
|
+
},
|
|
2116
|
+
schedule: {
|
|
2117
|
+
type: "string",
|
|
2118
|
+
description: "Cron schedule expression (e.g. '0 * * * *' for hourly)",
|
|
2119
|
+
},
|
|
2120
|
+
command: {
|
|
2121
|
+
type: "string",
|
|
2122
|
+
description: "Shell command to execute",
|
|
2123
|
+
},
|
|
2124
|
+
name: {
|
|
2125
|
+
type: "string",
|
|
2126
|
+
description: "Human-readable name for the cron job (optional)",
|
|
2127
|
+
},
|
|
1511
2128
|
},
|
|
1512
|
-
required: [
|
|
2129
|
+
required: ["computer_id", "schedule", "command"],
|
|
1513
2130
|
},
|
|
1514
2131
|
},
|
|
1515
2132
|
{
|
|
1516
|
-
name:
|
|
1517
|
-
description:
|
|
2133
|
+
name: "cron_get",
|
|
2134
|
+
description: "Get details of a specific cron job.",
|
|
1518
2135
|
inputSchema: {
|
|
1519
|
-
type:
|
|
2136
|
+
type: "object",
|
|
1520
2137
|
properties: {
|
|
1521
|
-
|
|
1522
|
-
key: { type: 'string', description: 'Object key' },
|
|
1523
|
-
expires_in: { type: 'integer', description: 'URL expiry in seconds (default: 3600)', default: 3600 },
|
|
1524
|
-
method: { type: 'string', enum: ['GET', 'PUT'], description: 'HTTP method (default: GET)', default: 'GET' },
|
|
2138
|
+
cron_id: { type: "string", description: "Cron job ID" },
|
|
1525
2139
|
},
|
|
1526
|
-
required: [
|
|
2140
|
+
required: ["cron_id"],
|
|
1527
2141
|
},
|
|
1528
2142
|
},
|
|
1529
|
-
// Databases
|
|
1530
|
-
{
|
|
1531
|
-
name: 'database_list',
|
|
1532
|
-
description: 'List all managed databases in the tenant.',
|
|
1533
|
-
inputSchema: { type: 'object', properties: {} },
|
|
1534
|
-
},
|
|
1535
2143
|
{
|
|
1536
|
-
name:
|
|
1537
|
-
description:
|
|
2144
|
+
name: "cron_delete",
|
|
2145
|
+
description: "Delete a cron job permanently.",
|
|
1538
2146
|
inputSchema: {
|
|
1539
|
-
type:
|
|
2147
|
+
type: "object",
|
|
1540
2148
|
properties: {
|
|
1541
|
-
|
|
1542
|
-
engine: { type: 'string', enum: ['postgres', 'mysql', 'redis'], description: 'Database engine' },
|
|
1543
|
-
version: { type: 'string', description: 'Engine version (optional)' },
|
|
1544
|
-
size: { type: 'string', description: 'Database size/tier (optional)' },
|
|
1545
|
-
region: { type: 'string', description: 'Region (optional)' },
|
|
2149
|
+
cron_id: { type: "string", description: "Cron job ID to delete" },
|
|
1546
2150
|
},
|
|
1547
|
-
required: [
|
|
2151
|
+
required: ["cron_id"],
|
|
1548
2152
|
},
|
|
1549
2153
|
},
|
|
1550
2154
|
{
|
|
1551
|
-
name:
|
|
1552
|
-
description:
|
|
2155
|
+
name: "cron_pause",
|
|
2156
|
+
description: "Pause a cron job so it stops running on schedule.",
|
|
1553
2157
|
inputSchema: {
|
|
1554
|
-
type:
|
|
2158
|
+
type: "object",
|
|
1555
2159
|
properties: {
|
|
1556
|
-
|
|
2160
|
+
cron_id: { type: "string", description: "Cron job ID to pause" },
|
|
1557
2161
|
},
|
|
1558
|
-
required: [
|
|
2162
|
+
required: ["cron_id"],
|
|
1559
2163
|
},
|
|
1560
2164
|
},
|
|
1561
2165
|
{
|
|
1562
|
-
name:
|
|
1563
|
-
description:
|
|
2166
|
+
name: "cron_resume",
|
|
2167
|
+
description: "Resume a paused cron job.",
|
|
1564
2168
|
inputSchema: {
|
|
1565
|
-
type:
|
|
2169
|
+
type: "object",
|
|
1566
2170
|
properties: {
|
|
1567
|
-
|
|
2171
|
+
cron_id: { type: "string", description: "Cron job ID to resume" },
|
|
1568
2172
|
},
|
|
1569
|
-
required: [
|
|
2173
|
+
required: ["cron_id"],
|
|
1570
2174
|
},
|
|
1571
2175
|
},
|
|
1572
2176
|
{
|
|
1573
|
-
name:
|
|
1574
|
-
description:
|
|
2177
|
+
name: "cron_run_now",
|
|
2178
|
+
description: "Trigger an immediate one-off execution of a cron job outside its schedule.",
|
|
1575
2179
|
inputSchema: {
|
|
1576
|
-
type:
|
|
2180
|
+
type: "object",
|
|
1577
2181
|
properties: {
|
|
1578
|
-
|
|
2182
|
+
cron_id: {
|
|
2183
|
+
type: "string",
|
|
2184
|
+
description: "Cron job ID to run immediately",
|
|
2185
|
+
},
|
|
1579
2186
|
},
|
|
1580
|
-
required: [
|
|
2187
|
+
required: ["cron_id"],
|
|
1581
2188
|
},
|
|
1582
2189
|
},
|
|
1583
2190
|
{
|
|
1584
|
-
name:
|
|
1585
|
-
description:
|
|
2191
|
+
name: "cron_executions",
|
|
2192
|
+
description: "List recent execution history for a cron job.",
|
|
1586
2193
|
inputSchema: {
|
|
1587
|
-
type:
|
|
2194
|
+
type: "object",
|
|
1588
2195
|
properties: {
|
|
1589
|
-
|
|
1590
|
-
lines: { type: 'integer', description: 'Number of log lines to return (default: 100)', default: 100 },
|
|
1591
|
-
since: { type: 'string', description: 'ISO 8601 timestamp to fetch logs from (optional)' },
|
|
2196
|
+
cron_id: { type: "string", description: "Cron job ID" },
|
|
1592
2197
|
},
|
|
1593
|
-
required: [
|
|
2198
|
+
required: ["cron_id"],
|
|
1594
2199
|
},
|
|
1595
2200
|
},
|
|
1596
|
-
//
|
|
2201
|
+
// Volumes
|
|
1597
2202
|
{
|
|
1598
|
-
name:
|
|
1599
|
-
description:
|
|
1600
|
-
inputSchema: { type:
|
|
2203
|
+
name: "volume_list",
|
|
2204
|
+
description: "List all volumes in the tenant.",
|
|
2205
|
+
inputSchema: { type: "object", properties: {} },
|
|
1601
2206
|
},
|
|
1602
2207
|
{
|
|
1603
|
-
name:
|
|
1604
|
-
description:
|
|
2208
|
+
name: "volume_create",
|
|
2209
|
+
description: "Create a new persistent volume.",
|
|
1605
2210
|
inputSchema: {
|
|
1606
|
-
type:
|
|
2211
|
+
type: "object",
|
|
1607
2212
|
properties: {
|
|
1608
|
-
name: {
|
|
1609
|
-
|
|
2213
|
+
name: {
|
|
2214
|
+
type: "string",
|
|
2215
|
+
description: "Human-readable name for the volume",
|
|
2216
|
+
},
|
|
2217
|
+
size_gb: {
|
|
2218
|
+
type: "integer",
|
|
2219
|
+
description: "Size of the volume in GB (optional)",
|
|
2220
|
+
},
|
|
2221
|
+
region: {
|
|
2222
|
+
type: "string",
|
|
2223
|
+
description: "Region to create the volume in (optional)",
|
|
2224
|
+
},
|
|
1610
2225
|
},
|
|
1611
|
-
required: [
|
|
2226
|
+
required: ["name"],
|
|
1612
2227
|
},
|
|
1613
2228
|
},
|
|
1614
2229
|
{
|
|
1615
|
-
name:
|
|
1616
|
-
description:
|
|
2230
|
+
name: "volume_get",
|
|
2231
|
+
description: "Get details of a specific volume.",
|
|
1617
2232
|
inputSchema: {
|
|
1618
|
-
type:
|
|
2233
|
+
type: "object",
|
|
1619
2234
|
properties: {
|
|
1620
|
-
|
|
2235
|
+
volume_id: { type: "string", description: "Volume ID" },
|
|
1621
2236
|
},
|
|
1622
|
-
required: [
|
|
2237
|
+
required: ["volume_id"],
|
|
1623
2238
|
},
|
|
1624
2239
|
},
|
|
1625
2240
|
{
|
|
1626
|
-
name:
|
|
1627
|
-
description: "
|
|
2241
|
+
name: "volume_delete",
|
|
2242
|
+
description: "Delete a volume permanently.",
|
|
1628
2243
|
inputSchema: {
|
|
1629
|
-
type:
|
|
2244
|
+
type: "object",
|
|
1630
2245
|
properties: {
|
|
1631
|
-
|
|
1632
|
-
name: { type: 'string', description: 'New workspace name (optional)' },
|
|
1633
|
-
description: { type: 'string', description: 'New description (optional)' },
|
|
2246
|
+
volume_id: { type: "string", description: "Volume ID to delete" },
|
|
1634
2247
|
},
|
|
1635
|
-
required: [
|
|
2248
|
+
required: ["volume_id"],
|
|
1636
2249
|
},
|
|
1637
2250
|
},
|
|
1638
2251
|
{
|
|
1639
|
-
name:
|
|
1640
|
-
description:
|
|
2252
|
+
name: "volume_attach",
|
|
2253
|
+
description: "Attach a volume to a computer at a given mount path.",
|
|
1641
2254
|
inputSchema: {
|
|
1642
|
-
type:
|
|
2255
|
+
type: "object",
|
|
1643
2256
|
properties: {
|
|
1644
|
-
|
|
2257
|
+
computer_id: {
|
|
2258
|
+
type: "string",
|
|
2259
|
+
description: "Computer ID to attach the volume to",
|
|
2260
|
+
},
|
|
2261
|
+
volume_id: { type: "string", description: "Volume ID to attach" },
|
|
2262
|
+
mount_path: {
|
|
2263
|
+
type: "string",
|
|
2264
|
+
description: "Path inside the VM to mount the volume (optional)",
|
|
2265
|
+
},
|
|
1645
2266
|
},
|
|
1646
|
-
required: [
|
|
2267
|
+
required: ["computer_id", "volume_id"],
|
|
1647
2268
|
},
|
|
1648
2269
|
},
|
|
1649
2270
|
{
|
|
1650
|
-
name:
|
|
1651
|
-
description:
|
|
2271
|
+
name: "volume_detach",
|
|
2272
|
+
description: "Detach a volume attachment from a computer.",
|
|
1652
2273
|
inputSchema: {
|
|
1653
|
-
type:
|
|
2274
|
+
type: "object",
|
|
1654
2275
|
properties: {
|
|
1655
|
-
|
|
1656
|
-
|
|
2276
|
+
computer_id: { type: "string", description: "Computer ID" },
|
|
2277
|
+
attachment_id: {
|
|
2278
|
+
type: "string",
|
|
2279
|
+
description: "Attachment ID to remove",
|
|
2280
|
+
},
|
|
1657
2281
|
},
|
|
1658
|
-
required: [
|
|
2282
|
+
required: ["computer_id", "attachment_id"],
|
|
1659
2283
|
},
|
|
1660
2284
|
},
|
|
1661
|
-
// Billing
|
|
1662
|
-
{
|
|
1663
|
-
name: 'billing_usage',
|
|
1664
|
-
description: 'Get current billing period usage for the tenant.',
|
|
1665
|
-
inputSchema: { type: 'object', properties: {} },
|
|
1666
|
-
},
|
|
1667
|
-
{
|
|
1668
|
-
name: 'billing_plan',
|
|
1669
|
-
description: 'Get the current billing plan details for the tenant.',
|
|
1670
|
-
inputSchema: { type: 'object', properties: {} },
|
|
1671
|
-
},
|
|
1672
2285
|
];
|
|
1673
2286
|
// ── Wire helpers ─────────────────────────────────────────────────────────────
|
|
1674
2287
|
function ok(text) {
|
|
@@ -1708,6 +2321,10 @@ async function dispatchTool(client, name, args) {
|
|
|
1708
2321
|
body["external_workspace_id"] = args["external_workspace_id"];
|
|
1709
2322
|
if (args["external_project_id"])
|
|
1710
2323
|
body["external_project_id"] = args["external_project_id"];
|
|
2324
|
+
if (args["gpu_model"]) {
|
|
2325
|
+
body["gpu_model"] = args["gpu_model"];
|
|
2326
|
+
body["gpu_count"] = args["gpu_count"] ?? 1;
|
|
2327
|
+
}
|
|
1711
2328
|
const computer = await client.apiPost("/api/v1/computers", body);
|
|
1712
2329
|
const data = unwrapData(computer);
|
|
1713
2330
|
const id = String(data["id"] ?? "");
|
|
@@ -2738,7 +3355,12 @@ async function dispatchTool(client, name, args) {
|
|
|
2738
3355
|
const lines = ["Deployments:"];
|
|
2739
3356
|
for (const d of items) {
|
|
2740
3357
|
const r = d;
|
|
2741
|
-
lines.push(" " +
|
|
3358
|
+
lines.push(" " +
|
|
3359
|
+
r["id"] +
|
|
3360
|
+
" " +
|
|
3361
|
+
r["name"] +
|
|
3362
|
+
" " +
|
|
3363
|
+
(r["status"] ?? r["state"] ?? ""));
|
|
2742
3364
|
}
|
|
2743
3365
|
return ok(lines.join("\n"));
|
|
2744
3366
|
}
|
|
@@ -2761,7 +3383,11 @@ async function dispatchTool(client, name, args) {
|
|
|
2761
3383
|
body["region"] = args["region"];
|
|
2762
3384
|
const result = await client.apiPost("/api/v1/deployments", body);
|
|
2763
3385
|
const data = unwrapData(result);
|
|
2764
|
-
return ok("Created deployment '" +
|
|
3386
|
+
return ok("Created deployment '" +
|
|
3387
|
+
String(data["name"] ?? args["name"]) +
|
|
3388
|
+
"' (id=" +
|
|
3389
|
+
data["id"] +
|
|
3390
|
+
")");
|
|
2765
3391
|
}
|
|
2766
3392
|
if (name === "deployment_delete") {
|
|
2767
3393
|
const did = String(args["deployment_id"] ?? "");
|
|
@@ -2779,7 +3405,11 @@ async function dispatchTool(client, name, args) {
|
|
|
2779
3405
|
body["source"] = args["source"];
|
|
2780
3406
|
const result = await client.apiPost("/api/v1/deployments/" + encodeURIComponent(did) + "/publish", body);
|
|
2781
3407
|
const data = unwrapData(result);
|
|
2782
|
-
return ok("Published deployment " +
|
|
3408
|
+
return ok("Published deployment " +
|
|
3409
|
+
did +
|
|
3410
|
+
" (version id=" +
|
|
3411
|
+
(data["id"] ?? "unknown") +
|
|
3412
|
+
")");
|
|
2783
3413
|
}
|
|
2784
3414
|
if (name === "deployment_rollback") {
|
|
2785
3415
|
const did = String(args["deployment_id"] ?? "");
|
|
@@ -2797,7 +3427,8 @@ async function dispatchTool(client, name, args) {
|
|
|
2797
3427
|
return err("deployment_id is required");
|
|
2798
3428
|
const result = await client.apiGet("/api/v1/deployments/" + encodeURIComponent(did) + "/env");
|
|
2799
3429
|
const envVars = unwrapData(result);
|
|
2800
|
-
if (!envVars ||
|
|
3430
|
+
if (!envVars ||
|
|
3431
|
+
(Array.isArray(envVars) && envVars.length === 0))
|
|
2801
3432
|
return ok("No environment variables set.");
|
|
2802
3433
|
const lines = ["Environment variables:"];
|
|
2803
3434
|
if (typeof envVars === "object" && !Array.isArray(envVars)) {
|
|
@@ -2823,13 +3454,20 @@ async function dispatchTool(client, name, args) {
|
|
|
2823
3454
|
const did = String(args["deployment_id"] ?? "");
|
|
2824
3455
|
if (!did)
|
|
2825
3456
|
return err("deployment_id is required");
|
|
2826
|
-
const params = new URLSearchParams({
|
|
3457
|
+
const params = new URLSearchParams({
|
|
3458
|
+
lines: String(args["lines"] ?? 100),
|
|
3459
|
+
});
|
|
2827
3460
|
if (args["since"])
|
|
2828
3461
|
params.set("since", String(args["since"]));
|
|
2829
|
-
const result = await client.apiGet("/api/v1/deployments/" +
|
|
3462
|
+
const result = await client.apiGet("/api/v1/deployments/" +
|
|
3463
|
+
encodeURIComponent(did) +
|
|
3464
|
+
"/logs?" +
|
|
3465
|
+
params.toString());
|
|
2830
3466
|
const logs = unwrapData(result);
|
|
2831
3467
|
if (Array.isArray(logs))
|
|
2832
|
-
return ok(logs.length
|
|
3468
|
+
return ok(logs.length
|
|
3469
|
+
? logs.map(String).join("\n")
|
|
3470
|
+
: "No logs.");
|
|
2833
3471
|
return ok(String(logs));
|
|
2834
3472
|
}
|
|
2835
3473
|
if (name === "deployment_version_list") {
|
|
@@ -2843,7 +3481,12 @@ async function dispatchTool(client, name, args) {
|
|
|
2843
3481
|
const lines = ["Versions:"];
|
|
2844
3482
|
for (const v of versions) {
|
|
2845
3483
|
const r = v;
|
|
2846
|
-
lines.push(" " +
|
|
3484
|
+
lines.push(" " +
|
|
3485
|
+
r["id"] +
|
|
3486
|
+
" " +
|
|
3487
|
+
(r["created_at"] ?? "") +
|
|
3488
|
+
" " +
|
|
3489
|
+
(r["status"] ?? ""));
|
|
2847
3490
|
}
|
|
2848
3491
|
return ok(lines.join("\n"));
|
|
2849
3492
|
}
|
|
@@ -2854,7 +3497,11 @@ async function dispatchTool(client, name, args) {
|
|
|
2854
3497
|
return err("deployment_id is required");
|
|
2855
3498
|
if (!vid)
|
|
2856
3499
|
return err("version_id is required");
|
|
2857
|
-
await client.apiPost("/api/v1/deployments/" +
|
|
3500
|
+
await client.apiPost("/api/v1/deployments/" +
|
|
3501
|
+
encodeURIComponent(did) +
|
|
3502
|
+
"/versions/" +
|
|
3503
|
+
encodeURIComponent(vid) +
|
|
3504
|
+
"/promote", {});
|
|
2858
3505
|
return ok("Version " + vid + " promoted on deployment " + did + ".");
|
|
2859
3506
|
}
|
|
2860
3507
|
// ── Storage ───────────────────────────────────────────────────────────
|
|
@@ -2878,7 +3525,11 @@ async function dispatchTool(client, name, args) {
|
|
|
2878
3525
|
body["public"] = args["public"];
|
|
2879
3526
|
const result = await client.apiPost("/api/v1/storage/buckets", body);
|
|
2880
3527
|
const data = unwrapData(result);
|
|
2881
|
-
return ok("Created bucket '" +
|
|
3528
|
+
return ok("Created bucket '" +
|
|
3529
|
+
String(data["name"] ?? args["name"]) +
|
|
3530
|
+
"' (id=" +
|
|
3531
|
+
data["id"] +
|
|
3532
|
+
")");
|
|
2882
3533
|
}
|
|
2883
3534
|
if (name === "storage_bucket_delete") {
|
|
2884
3535
|
const bid = String(args["bucket_id"] ?? "");
|
|
@@ -2902,7 +3553,12 @@ async function dispatchTool(client, name, args) {
|
|
|
2902
3553
|
const lines = ["Objects:"];
|
|
2903
3554
|
for (const o of items) {
|
|
2904
3555
|
const r = o;
|
|
2905
|
-
lines.push(" " +
|
|
3556
|
+
lines.push(" " +
|
|
3557
|
+
r["key"] +
|
|
3558
|
+
" " +
|
|
3559
|
+
(r["size"] ?? "") +
|
|
3560
|
+
" " +
|
|
3561
|
+
(r["last_modified"] ?? ""));
|
|
2906
3562
|
}
|
|
2907
3563
|
return ok(lines.join("\n"));
|
|
2908
3564
|
}
|
|
@@ -2921,7 +3577,10 @@ async function dispatchTool(client, name, args) {
|
|
|
2921
3577
|
const bid = String(args["bucket_id"] ?? "");
|
|
2922
3578
|
if (!bid)
|
|
2923
3579
|
return err("bucket_id is required");
|
|
2924
|
-
const result = await client.apiGet("/api/v1/storage/buckets/" +
|
|
3580
|
+
const result = await client.apiGet("/api/v1/storage/buckets/" +
|
|
3581
|
+
encodeURIComponent(bid) +
|
|
3582
|
+
"/objects/" +
|
|
3583
|
+
encodeURIComponent(String(args["key"] ?? "")));
|
|
2925
3584
|
const data = unwrapData(result);
|
|
2926
3585
|
const content = typeof data["content"] === "string"
|
|
2927
3586
|
? Buffer.from(data["content"], "base64").toString("utf8")
|
|
@@ -2934,7 +3593,10 @@ async function dispatchTool(client, name, args) {
|
|
|
2934
3593
|
const bid = String(args["bucket_id"] ?? "");
|
|
2935
3594
|
if (!bid)
|
|
2936
3595
|
return err("bucket_id is required");
|
|
2937
|
-
await client.apiDelete("/api/v1/storage/buckets/" +
|
|
3596
|
+
await client.apiDelete("/api/v1/storage/buckets/" +
|
|
3597
|
+
encodeURIComponent(bid) +
|
|
3598
|
+
"/objects/" +
|
|
3599
|
+
encodeURIComponent(String(args["key"] ?? "")));
|
|
2938
3600
|
return ok("Deleted " + String(args["key"]) + " from bucket " + bid + ".");
|
|
2939
3601
|
}
|
|
2940
3602
|
if (name === "storage_presign") {
|
|
@@ -2958,19 +3620,33 @@ async function dispatchTool(client, name, args) {
|
|
|
2958
3620
|
const lines = ["Databases:"];
|
|
2959
3621
|
for (const d of items) {
|
|
2960
3622
|
const r = d;
|
|
2961
|
-
lines.push(" " +
|
|
3623
|
+
lines.push(" " +
|
|
3624
|
+
r["id"] +
|
|
3625
|
+
" " +
|
|
3626
|
+
r["name"] +
|
|
3627
|
+
" " +
|
|
3628
|
+
(r["engine"] ?? "") +
|
|
3629
|
+
" " +
|
|
3630
|
+
(r["status"] ?? ""));
|
|
2962
3631
|
}
|
|
2963
3632
|
return ok(lines.join("\n"));
|
|
2964
3633
|
}
|
|
2965
3634
|
if (name === "database_create") {
|
|
2966
|
-
const body = {
|
|
3635
|
+
const body = {
|
|
3636
|
+
name: args["name"],
|
|
3637
|
+
engine: args["engine"],
|
|
3638
|
+
};
|
|
2967
3639
|
for (const key of ["version", "size", "region"]) {
|
|
2968
3640
|
if (args[key])
|
|
2969
3641
|
body[key] = args[key];
|
|
2970
3642
|
}
|
|
2971
3643
|
const result = await client.apiPost("/api/v1/databases", body);
|
|
2972
3644
|
const data = unwrapData(result);
|
|
2973
|
-
return ok("Created database '" +
|
|
3645
|
+
return ok("Created database '" +
|
|
3646
|
+
String(data["name"] ?? args["name"]) +
|
|
3647
|
+
"' (id=" +
|
|
3648
|
+
data["id"] +
|
|
3649
|
+
")");
|
|
2974
3650
|
}
|
|
2975
3651
|
if (name === "database_get") {
|
|
2976
3652
|
const dbid = String(args["database_id"] ?? "");
|
|
@@ -2993,7 +3669,14 @@ async function dispatchTool(client, name, args) {
|
|
|
2993
3669
|
const result = await client.apiGet("/api/v1/databases/" + encodeURIComponent(dbid) + "/credentials");
|
|
2994
3670
|
const data = unwrapData(result);
|
|
2995
3671
|
const lines = ["Database credentials:"];
|
|
2996
|
-
for (const field of [
|
|
3672
|
+
for (const field of [
|
|
3673
|
+
"connection_string",
|
|
3674
|
+
"host",
|
|
3675
|
+
"port",
|
|
3676
|
+
"database",
|
|
3677
|
+
"username",
|
|
3678
|
+
"password",
|
|
3679
|
+
]) {
|
|
2997
3680
|
if (data[field])
|
|
2998
3681
|
lines.push(" " + field + ": " + data[field]);
|
|
2999
3682
|
}
|
|
@@ -3003,13 +3686,20 @@ async function dispatchTool(client, name, args) {
|
|
|
3003
3686
|
const dbid = String(args["database_id"] ?? "");
|
|
3004
3687
|
if (!dbid)
|
|
3005
3688
|
return err("database_id is required");
|
|
3006
|
-
const params = new URLSearchParams({
|
|
3689
|
+
const params = new URLSearchParams({
|
|
3690
|
+
lines: String(args["lines"] ?? 100),
|
|
3691
|
+
});
|
|
3007
3692
|
if (args["since"])
|
|
3008
3693
|
params.set("since", String(args["since"]));
|
|
3009
|
-
const result = await client.apiGet("/api/v1/databases/" +
|
|
3694
|
+
const result = await client.apiGet("/api/v1/databases/" +
|
|
3695
|
+
encodeURIComponent(dbid) +
|
|
3696
|
+
"/logs?" +
|
|
3697
|
+
params.toString());
|
|
3010
3698
|
const logs = unwrapData(result);
|
|
3011
3699
|
if (Array.isArray(logs))
|
|
3012
|
-
return ok(logs.length
|
|
3700
|
+
return ok(logs.length
|
|
3701
|
+
? logs.map(String).join("\n")
|
|
3702
|
+
: "No logs.");
|
|
3013
3703
|
return ok(String(logs));
|
|
3014
3704
|
}
|
|
3015
3705
|
// ── Workspaces ────────────────────────────────────────────────────────
|
|
@@ -3031,7 +3721,11 @@ async function dispatchTool(client, name, args) {
|
|
|
3031
3721
|
body["description"] = args["description"];
|
|
3032
3722
|
const result = await client.apiPost("/api/v1/workspaces", body);
|
|
3033
3723
|
const data = unwrapData(result);
|
|
3034
|
-
return ok("Created workspace '" +
|
|
3724
|
+
return ok("Created workspace '" +
|
|
3725
|
+
String(data["name"] ?? args["name"]) +
|
|
3726
|
+
"' (id=" +
|
|
3727
|
+
data["id"] +
|
|
3728
|
+
")");
|
|
3035
3729
|
}
|
|
3036
3730
|
if (name === "workspace_get") {
|
|
3037
3731
|
const wid = String(args["workspace_id"] ?? "");
|
|
@@ -3079,6 +3773,425 @@ async function dispatchTool(client, name, args) {
|
|
|
3079
3773
|
const result = await client.apiGet("/api/v1/billing/plan");
|
|
3080
3774
|
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
3081
3775
|
}
|
|
3776
|
+
// ── Tunnels / Port forwarding ─────────────────────────────────────────
|
|
3777
|
+
if (name === "computer_expose_port") {
|
|
3778
|
+
if (!cid)
|
|
3779
|
+
return err("computer_id is required");
|
|
3780
|
+
const body = { port: args["port"] };
|
|
3781
|
+
if (args["protocol"])
|
|
3782
|
+
body["protocol"] = args["protocol"];
|
|
3783
|
+
const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(cid)}/ports`, body);
|
|
3784
|
+
const data = unwrapData(result);
|
|
3785
|
+
const url = String(data["url"] ?? data["public_url"] ?? "");
|
|
3786
|
+
return ok(`Port ${args["port"]} exposed. URL: ${url}`);
|
|
3787
|
+
}
|
|
3788
|
+
if (name === "computer_list_ports") {
|
|
3789
|
+
if (!cid)
|
|
3790
|
+
return err("computer_id is required");
|
|
3791
|
+
const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/ports`);
|
|
3792
|
+
const items = listOf(result);
|
|
3793
|
+
if (items.length === 0)
|
|
3794
|
+
return ok("No ports currently exposed.");
|
|
3795
|
+
const lines = ["Exposed ports:"];
|
|
3796
|
+
for (const p of items) {
|
|
3797
|
+
const r = p;
|
|
3798
|
+
lines.push(` port=${r["port"]} protocol=${r["protocol"] ?? "http"} url=${r["url"] ?? r["public_url"] ?? ""}`);
|
|
3799
|
+
}
|
|
3800
|
+
return ok(lines.join("\n"));
|
|
3801
|
+
}
|
|
3802
|
+
if (name === "computer_preview_url") {
|
|
3803
|
+
if (!cid)
|
|
3804
|
+
return err("computer_id is required");
|
|
3805
|
+
const port = args["port"];
|
|
3806
|
+
const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/ports/${encodeURIComponent(String(port))}/url`);
|
|
3807
|
+
const data = unwrapData(result);
|
|
3808
|
+
const url = String(data["url"] ?? data["public_url"] ?? "") ||
|
|
3809
|
+
`https://${port}-${cid}.computer.miosa.ai`;
|
|
3810
|
+
return ok(`Preview URL: ${url}`);
|
|
3811
|
+
}
|
|
3812
|
+
// ── Network policy ────────────────────────────────────────────────────
|
|
3813
|
+
if (name === "computer_network_policy_get") {
|
|
3814
|
+
if (!cid)
|
|
3815
|
+
return err("computer_id is required");
|
|
3816
|
+
const result = await client.apiGet(`/api/v1/computers/${encodeURIComponent(cid)}/network-policy`);
|
|
3817
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
3818
|
+
}
|
|
3819
|
+
if (name === "computer_network_policy_set") {
|
|
3820
|
+
if (!cid)
|
|
3821
|
+
return err("computer_id is required");
|
|
3822
|
+
const body = { rules: args["rules"] };
|
|
3823
|
+
if (args["default_effect"])
|
|
3824
|
+
body["default_effect"] = args["default_effect"];
|
|
3825
|
+
await client.apiPut(`/api/v1/computers/${encodeURIComponent(cid)}/network-policy`, body);
|
|
3826
|
+
return ok(`Network policy updated for computer ${cid}.`);
|
|
3827
|
+
}
|
|
3828
|
+
if (name === "computer_network_policy_reset") {
|
|
3829
|
+
if (!cid)
|
|
3830
|
+
return err("computer_id is required");
|
|
3831
|
+
await client.apiDelete(`/api/v1/computers/${encodeURIComponent(cid)}/network-policy`);
|
|
3832
|
+
return ok(`Network policy reset to default for computer ${cid}.`);
|
|
3833
|
+
}
|
|
3834
|
+
// ── Webhooks ──────────────────────────────────────────────────────────
|
|
3835
|
+
if (name === "webhook_list") {
|
|
3836
|
+
const result = await client.apiGet("/api/v1/webhooks");
|
|
3837
|
+
const items = listOf(result);
|
|
3838
|
+
if (items.length === 0)
|
|
3839
|
+
return ok("No webhooks found.");
|
|
3840
|
+
const lines = ["Webhooks:"];
|
|
3841
|
+
for (const w of items) {
|
|
3842
|
+
const r = w;
|
|
3843
|
+
lines.push(` ${r["id"]} ${r["url"]} events=${JSON.stringify(r["events"] ?? [])}`);
|
|
3844
|
+
}
|
|
3845
|
+
return ok(lines.join("\n"));
|
|
3846
|
+
}
|
|
3847
|
+
if (name === "webhook_create") {
|
|
3848
|
+
const result = await client.apiPost("/api/v1/webhooks", {
|
|
3849
|
+
url: args["url"],
|
|
3850
|
+
events: args["events"],
|
|
3851
|
+
});
|
|
3852
|
+
const data = unwrapData(result);
|
|
3853
|
+
return ok(`Created webhook (id=${data["id"] ?? "?"}).`);
|
|
3854
|
+
}
|
|
3855
|
+
if (name === "webhook_delete") {
|
|
3856
|
+
const whId = String(args["webhook_id"] ?? "");
|
|
3857
|
+
if (!whId)
|
|
3858
|
+
return err("webhook_id is required");
|
|
3859
|
+
await client.apiDelete(`/api/v1/webhooks/${encodeURIComponent(whId)}`);
|
|
3860
|
+
return ok(`Webhook ${whId} deleted.`);
|
|
3861
|
+
}
|
|
3862
|
+
if (name === "webhook_test") {
|
|
3863
|
+
const whId = String(args["webhook_id"] ?? "");
|
|
3864
|
+
if (!whId)
|
|
3865
|
+
return err("webhook_id is required");
|
|
3866
|
+
const result = await client.apiPost(`/api/v1/webhooks/${encodeURIComponent(whId)}/test`, {});
|
|
3867
|
+
const data = unwrapData(result);
|
|
3868
|
+
const status = String(data["status"] ?? "delivered");
|
|
3869
|
+
return ok(`Test event sent to webhook ${whId} (status=${status}).`);
|
|
3870
|
+
}
|
|
3871
|
+
// ── Functions ─────────────────────────────────────────────────────────
|
|
3872
|
+
if (name === "function_list") {
|
|
3873
|
+
const result = await client.apiGet("/api/v1/functions");
|
|
3874
|
+
const items = listOf(result);
|
|
3875
|
+
if (items.length === 0)
|
|
3876
|
+
return ok("No functions found.");
|
|
3877
|
+
const lines = ["Functions:"];
|
|
3878
|
+
for (const f of items) {
|
|
3879
|
+
const r = f;
|
|
3880
|
+
lines.push(` ${r["id"]} ${r["name"]} runtime=${r["runtime"] ?? ""}`);
|
|
3881
|
+
}
|
|
3882
|
+
return ok(lines.join("\n"));
|
|
3883
|
+
}
|
|
3884
|
+
if (name === "function_create") {
|
|
3885
|
+
const body = {
|
|
3886
|
+
name: args["name"],
|
|
3887
|
+
runtime: args["runtime"],
|
|
3888
|
+
};
|
|
3889
|
+
if (args["code"])
|
|
3890
|
+
body["code"] = args["code"];
|
|
3891
|
+
const result = await client.apiPost("/api/v1/functions", body);
|
|
3892
|
+
const data = unwrapData(result);
|
|
3893
|
+
return ok(`Created function '${data["name"] ?? args["name"]}' (id=${data["id"]}).`);
|
|
3894
|
+
}
|
|
3895
|
+
if (name === "function_invoke") {
|
|
3896
|
+
const fnId = String(args["function_id"] ?? "");
|
|
3897
|
+
if (!fnId)
|
|
3898
|
+
return err("function_id is required");
|
|
3899
|
+
const body = {};
|
|
3900
|
+
if (args["payload"] !== undefined)
|
|
3901
|
+
body["payload"] = args["payload"];
|
|
3902
|
+
const result = await client.apiPost(`/api/v1/functions/${encodeURIComponent(fnId)}/invoke`, body);
|
|
3903
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
3904
|
+
}
|
|
3905
|
+
if (name === "function_delete") {
|
|
3906
|
+
const fnId = String(args["function_id"] ?? "");
|
|
3907
|
+
if (!fnId)
|
|
3908
|
+
return err("function_id is required");
|
|
3909
|
+
await client.apiDelete(`/api/v1/functions/${encodeURIComponent(fnId)}`);
|
|
3910
|
+
return ok(`Function ${fnId} deleted.`);
|
|
3911
|
+
}
|
|
3912
|
+
// ── API Keys ──────────────────────────────────────────────────────────
|
|
3913
|
+
if (name === "api_key_list") {
|
|
3914
|
+
const result = await client.apiGet("/api/v1/api-keys");
|
|
3915
|
+
const items = listOf(result);
|
|
3916
|
+
if (items.length === 0)
|
|
3917
|
+
return ok("No API keys found.");
|
|
3918
|
+
const lines = ["API keys:"];
|
|
3919
|
+
for (const k of items) {
|
|
3920
|
+
const r = k;
|
|
3921
|
+
const scopes = Array.isArray(r["scopes"])
|
|
3922
|
+
? r["scopes"].join(", ")
|
|
3923
|
+
: String(r["scopes"] ?? "");
|
|
3924
|
+
lines.push(` ${r["id"]} ${r["name"]} scopes=[${scopes}]`);
|
|
3925
|
+
}
|
|
3926
|
+
return ok(lines.join("\n"));
|
|
3927
|
+
}
|
|
3928
|
+
if (name === "api_key_create") {
|
|
3929
|
+
const body = { name: args["name"] };
|
|
3930
|
+
if (args["scopes"])
|
|
3931
|
+
body["scopes"] = args["scopes"];
|
|
3932
|
+
const result = await client.apiPost("/api/v1/api-keys", body);
|
|
3933
|
+
const data = unwrapData(result);
|
|
3934
|
+
const keyId = String(data["id"] ?? "?");
|
|
3935
|
+
const keyValue = String(data["key"] ?? data["token"] ?? data["secret"] ?? "");
|
|
3936
|
+
let msg = `Created API key '${args["name"]}' (id=${keyId}).`;
|
|
3937
|
+
if (keyValue)
|
|
3938
|
+
msg += `\nKey value (shown once): ${keyValue}`;
|
|
3939
|
+
return ok(msg);
|
|
3940
|
+
}
|
|
3941
|
+
if (name === "api_key_delete") {
|
|
3942
|
+
const keyId = String(args["key_id"] ?? "");
|
|
3943
|
+
if (!keyId)
|
|
3944
|
+
return err("key_id is required");
|
|
3945
|
+
await client.apiDelete(`/api/v1/api-keys/${encodeURIComponent(keyId)}`);
|
|
3946
|
+
return ok(`API key ${keyId} deleted.`);
|
|
3947
|
+
}
|
|
3948
|
+
// ── Cron jobs ─────────────────────────────────────────────────────────
|
|
3949
|
+
if (name === "cron_list") {
|
|
3950
|
+
const params = new URLSearchParams();
|
|
3951
|
+
if (args["computer_id"])
|
|
3952
|
+
params.set("computer_id", String(args["computer_id"]));
|
|
3953
|
+
const qs = params.toString() ? `?${params.toString()}` : "";
|
|
3954
|
+
const result = await client.apiGet(`/api/v1/cron-jobs${qs}`);
|
|
3955
|
+
const items = listOf(result);
|
|
3956
|
+
if (items.length === 0)
|
|
3957
|
+
return ok("No cron jobs found.");
|
|
3958
|
+
const lines = ["Cron jobs:"];
|
|
3959
|
+
for (const j of items) {
|
|
3960
|
+
const r = j;
|
|
3961
|
+
lines.push(` ${r["id"]} ${r["name"] ?? ""} ${r["schedule"] ?? ""} status=${r["status"] ?? r["state"] ?? ""}`);
|
|
3962
|
+
}
|
|
3963
|
+
return ok(lines.join("\n"));
|
|
3964
|
+
}
|
|
3965
|
+
if (name === "cron_create") {
|
|
3966
|
+
const body = {
|
|
3967
|
+
computer_id: args["computer_id"],
|
|
3968
|
+
schedule: args["schedule"],
|
|
3969
|
+
command: args["command"],
|
|
3970
|
+
};
|
|
3971
|
+
if (args["name"])
|
|
3972
|
+
body["name"] = args["name"];
|
|
3973
|
+
const result = await client.apiPost("/api/v1/cron-jobs", body);
|
|
3974
|
+
const data = unwrapData(result);
|
|
3975
|
+
return ok(`Created cron job '${data["name"] ?? args["name"] ?? ""}' (id=${data["id"]}).`);
|
|
3976
|
+
}
|
|
3977
|
+
if (name === "cron_get") {
|
|
3978
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
3979
|
+
if (!cronId)
|
|
3980
|
+
return err("cron_id is required");
|
|
3981
|
+
const result = await client.apiGet(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}`);
|
|
3982
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
3983
|
+
}
|
|
3984
|
+
if (name === "cron_delete") {
|
|
3985
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
3986
|
+
if (!cronId)
|
|
3987
|
+
return err("cron_id is required");
|
|
3988
|
+
await client.apiDelete(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}`);
|
|
3989
|
+
return ok(`Cron job ${cronId} deleted.`);
|
|
3990
|
+
}
|
|
3991
|
+
if (name === "cron_pause") {
|
|
3992
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
3993
|
+
if (!cronId)
|
|
3994
|
+
return err("cron_id is required");
|
|
3995
|
+
await client.apiPost(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}/pause`, {});
|
|
3996
|
+
return ok(`Cron job ${cronId} paused.`);
|
|
3997
|
+
}
|
|
3998
|
+
if (name === "cron_resume") {
|
|
3999
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
4000
|
+
if (!cronId)
|
|
4001
|
+
return err("cron_id is required");
|
|
4002
|
+
await client.apiPost(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}/resume`, {});
|
|
4003
|
+
return ok(`Cron job ${cronId} resumed.`);
|
|
4004
|
+
}
|
|
4005
|
+
if (name === "cron_run_now") {
|
|
4006
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
4007
|
+
if (!cronId)
|
|
4008
|
+
return err("cron_id is required");
|
|
4009
|
+
const result = await client.apiPost(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}/run-now`, {});
|
|
4010
|
+
const data = unwrapData(result);
|
|
4011
|
+
const execId = data["id"] ?? data["execution_id"];
|
|
4012
|
+
return ok(`Cron job ${cronId} triggered.${execId ? ` Execution id=${execId}.` : ""}`);
|
|
4013
|
+
}
|
|
4014
|
+
if (name === "cron_executions") {
|
|
4015
|
+
const cronId = String(args["cron_id"] ?? "");
|
|
4016
|
+
if (!cronId)
|
|
4017
|
+
return err("cron_id is required");
|
|
4018
|
+
const result = await client.apiGet(`/api/v1/cron-jobs/${encodeURIComponent(cronId)}/executions`);
|
|
4019
|
+
const items = listOf(result);
|
|
4020
|
+
if (items.length === 0)
|
|
4021
|
+
return ok("No executions found.");
|
|
4022
|
+
const lines = ["Executions:"];
|
|
4023
|
+
for (const e of items) {
|
|
4024
|
+
const r = e;
|
|
4025
|
+
lines.push(` ${r["id"]} ${r["started_at"] ?? r["created_at"] ?? ""} status=${r["status"] ?? ""} exit_code=${r["exit_code"] ?? ""}`);
|
|
4026
|
+
}
|
|
4027
|
+
return ok(lines.join("\n"));
|
|
4028
|
+
}
|
|
4029
|
+
// ── Regions ────────────────────────────────────────────────────────────
|
|
4030
|
+
if (name === "region_list" || name === "computer_list_regions") {
|
|
4031
|
+
const result = await client.apiGet("/api/v1/regions");
|
|
4032
|
+
const regions = (() => {
|
|
4033
|
+
const d = unwrapData(result);
|
|
4034
|
+
if (Array.isArray(d))
|
|
4035
|
+
return d;
|
|
4036
|
+
const r = d;
|
|
4037
|
+
for (const key of ["regions", "data", "items"]) {
|
|
4038
|
+
if (Array.isArray(r[key]))
|
|
4039
|
+
return r[key];
|
|
4040
|
+
}
|
|
4041
|
+
return Object.values(r);
|
|
4042
|
+
})();
|
|
4043
|
+
if (regions.length === 0)
|
|
4044
|
+
return ok("No regions found.");
|
|
4045
|
+
const lines = ["Regions:"];
|
|
4046
|
+
for (const region of regions) {
|
|
4047
|
+
const r = region;
|
|
4048
|
+
const gpuTypes = r["gpu_types"] ?? r["gpus"] ?? [];
|
|
4049
|
+
const gpuInfo = Array.isArray(gpuTypes) && gpuTypes.length > 0
|
|
4050
|
+
? ` gpus=${JSON.stringify(gpuTypes)}`
|
|
4051
|
+
: "";
|
|
4052
|
+
lines.push(` ${r["id"] ?? r["slug"] ?? ""} ${r["name"] ?? ""} status=${r["status"] ?? "available"}${gpuInfo}`);
|
|
4053
|
+
}
|
|
4054
|
+
return ok(lines.join("\n"));
|
|
4055
|
+
}
|
|
4056
|
+
// ── Computer templates ─────────────────────────────────────────────────
|
|
4057
|
+
if (name === "computer_template_list") {
|
|
4058
|
+
const wid = String(args["workspace_id"] ?? "");
|
|
4059
|
+
if (!wid)
|
|
4060
|
+
return err("workspace_id is required");
|
|
4061
|
+
const result = await client.apiGet(`/api/v1/workspaces/${encodeURIComponent(wid)}/computer-templates`);
|
|
4062
|
+
const items = listOf(result);
|
|
4063
|
+
if (items.length === 0)
|
|
4064
|
+
return ok("No computer templates found.");
|
|
4065
|
+
const lines = ["Computer templates:"];
|
|
4066
|
+
for (const t of items) {
|
|
4067
|
+
const r = t;
|
|
4068
|
+
lines.push(` ${r["id"]} ${r["name"]} type=${r["template_type"] ?? ""} size=${r["size"] ?? ""}`);
|
|
4069
|
+
}
|
|
4070
|
+
return ok(lines.join("\n"));
|
|
4071
|
+
}
|
|
4072
|
+
if (name === "computer_template_create") {
|
|
4073
|
+
const wid = String(args["workspace_id"] ?? "");
|
|
4074
|
+
if (!wid)
|
|
4075
|
+
return err("workspace_id is required");
|
|
4076
|
+
const body = { name: args["name"] };
|
|
4077
|
+
if (args["template_type"])
|
|
4078
|
+
body["template_type"] = args["template_type"];
|
|
4079
|
+
if (args["size"])
|
|
4080
|
+
body["size"] = args["size"];
|
|
4081
|
+
if (args["selected_apps"])
|
|
4082
|
+
body["selected_apps"] = args["selected_apps"];
|
|
4083
|
+
if (args["settings"])
|
|
4084
|
+
body["settings"] = args["settings"];
|
|
4085
|
+
const result = await client.apiPost(`/api/v1/workspaces/${encodeURIComponent(wid)}/computer-templates`, body);
|
|
4086
|
+
const data = unwrapData(result);
|
|
4087
|
+
return ok(`Created computer template '${data["name"] ?? args["name"]}' (id=${data["id"]}).`);
|
|
4088
|
+
}
|
|
4089
|
+
// ── Settings ───────────────────────────────────────────────────────────
|
|
4090
|
+
if (name === "settings_get") {
|
|
4091
|
+
const result = await client.apiGet("/api/v1/settings");
|
|
4092
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
4093
|
+
}
|
|
4094
|
+
if (name === "settings_get_branding") {
|
|
4095
|
+
const result = await client.apiGet("/api/v1/settings/branding");
|
|
4096
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
4097
|
+
}
|
|
4098
|
+
if (name === "settings_update_branding") {
|
|
4099
|
+
const body = {};
|
|
4100
|
+
if (args["desktop_wallpaper_url"])
|
|
4101
|
+
body["desktop_wallpaper_url"] = args["desktop_wallpaper_url"];
|
|
4102
|
+
if (args["logo_url"])
|
|
4103
|
+
body["logo_url"] = args["logo_url"];
|
|
4104
|
+
const result = await client.apiPut("/api/v1/settings/branding", body);
|
|
4105
|
+
return ok(`Branding updated: ${JSON.stringify(unwrapData(result), null, 2)}`);
|
|
4106
|
+
}
|
|
4107
|
+
if (name === "settings_compute_pricing") {
|
|
4108
|
+
const result = await client.apiGet("/api/v1/settings/compute-pricing");
|
|
4109
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
4110
|
+
}
|
|
4111
|
+
// ── Sandbox template extensions ────────────────────────────────────────
|
|
4112
|
+
if (name === "sandbox_template_get") {
|
|
4113
|
+
const tid = String(args["template_id"] ?? "");
|
|
4114
|
+
if (!tid)
|
|
4115
|
+
return err("template_id is required");
|
|
4116
|
+
const result = await client.apiGet(`/api/v1/sandbox-templates/${encodeURIComponent(tid)}`);
|
|
4117
|
+
return ok(JSON.stringify(unwrapData(result), null, 2));
|
|
4118
|
+
}
|
|
4119
|
+
if (name === "sandbox_template_builds") {
|
|
4120
|
+
const tid = String(args["template_id"] ?? "");
|
|
4121
|
+
if (!tid)
|
|
4122
|
+
return err("template_id is required");
|
|
4123
|
+
const result = await client.apiGet(`/api/v1/sandbox-templates/${encodeURIComponent(tid)}/builds`);
|
|
4124
|
+
const items = listOf(result);
|
|
4125
|
+
if (items.length === 0)
|
|
4126
|
+
return ok("No builds found.");
|
|
4127
|
+
const lines = ["Builds:"];
|
|
4128
|
+
for (const b of items) {
|
|
4129
|
+
const r = b;
|
|
4130
|
+
lines.push(` ${r["id"]} ${r["status"] ?? ""} created_at=${r["created_at"] ?? ""}`);
|
|
4131
|
+
}
|
|
4132
|
+
return ok(lines.join("\n"));
|
|
4133
|
+
}
|
|
4134
|
+
// ── Volumes ───────────────────────────────────────────────────────────
|
|
4135
|
+
if (name === "volume_list") {
|
|
4136
|
+
const result = await client.apiGet("/api/v1/volumes");
|
|
4137
|
+
const items = listOf(result);
|
|
4138
|
+
if (items.length === 0)
|
|
4139
|
+
return ok("No volumes found.");
|
|
4140
|
+
const lines = ["Volumes:"];
|
|
4141
|
+
for (const v of items) {
|
|
4142
|
+
const r = v;
|
|
4143
|
+
lines.push(` ${r["id"]} ${r["name"]} size_gb=${r["size_gb"] ?? ""} region=${r["region"] ?? ""} status=${r["status"] ?? ""}`);
|
|
4144
|
+
}
|
|
4145
|
+
return ok(lines.join("\n"));
|
|
4146
|
+
}
|
|
4147
|
+
if (name === "volume_create") {
|
|
4148
|
+
const body = { name: args["name"] };
|
|
4149
|
+
if (args["size_gb"] !== undefined)
|
|
4150
|
+
body["size_gb"] = args["size_gb"];
|
|
4151
|
+
if (args["region"])
|
|
4152
|
+
body["region"] = args["region"];
|
|
4153
|
+
const result = await client.apiPost("/api/v1/volumes", body);
|
|
4154
|
+
const data = unwrapData(result);
|
|
4155
|
+
return ok(`Created volume '${data["name"] ?? args["name"]}' (id=${data["id"]}).`);
|
|
4156
|
+
}
|
|
4157
|
+
if (name === "volume_get") {
|
|
4158
|
+
const vid = String(args["volume_id"] ?? "");
|
|
4159
|
+
if (!vid)
|
|
4160
|
+
return err("volume_id is required");
|
|
4161
|
+
const result = await client.apiGet(`/api/v1/volumes/${encodeURIComponent(vid)}`);
|
|
4162
|
+
const data = unwrapData(result);
|
|
4163
|
+
return ok(`id=${data["id"]} name=${JSON.stringify(data["name"])} size_gb=${data["size_gb"] ?? ""} region=${data["region"] ?? ""} status=${data["status"] ?? ""}`);
|
|
4164
|
+
}
|
|
4165
|
+
if (name === "volume_delete") {
|
|
4166
|
+
const vid = String(args["volume_id"] ?? "");
|
|
4167
|
+
if (!vid)
|
|
4168
|
+
return err("volume_id is required");
|
|
4169
|
+
await client.apiDelete(`/api/v1/volumes/${encodeURIComponent(vid)}`);
|
|
4170
|
+
return ok(`Volume ${vid} deleted.`);
|
|
4171
|
+
}
|
|
4172
|
+
if (name === "volume_attach") {
|
|
4173
|
+
const attachCid = String(args["computer_id"] ?? "");
|
|
4174
|
+
if (!attachCid)
|
|
4175
|
+
return err("computer_id is required");
|
|
4176
|
+
const attachBody = {
|
|
4177
|
+
volume_id: args["volume_id"],
|
|
4178
|
+
};
|
|
4179
|
+
if (args["mount_path"])
|
|
4180
|
+
attachBody["mount_path"] = args["mount_path"];
|
|
4181
|
+
const result = await client.apiPost(`/api/v1/computers/${encodeURIComponent(attachCid)}/volumes`, attachBody);
|
|
4182
|
+
const data = unwrapData(result);
|
|
4183
|
+
return ok(`Volume ${args["volume_id"]} attached to computer ${attachCid} (attachment id=${data["id"] ?? "?"}).`);
|
|
4184
|
+
}
|
|
4185
|
+
if (name === "volume_detach") {
|
|
4186
|
+
const detachCid = String(args["computer_id"] ?? "");
|
|
4187
|
+
if (!detachCid)
|
|
4188
|
+
return err("computer_id is required");
|
|
4189
|
+
const attId = String(args["attachment_id"] ?? "");
|
|
4190
|
+
if (!attId)
|
|
4191
|
+
return err("attachment_id is required");
|
|
4192
|
+
await client.apiDelete(`/api/v1/computers/${encodeURIComponent(detachCid)}/volumes/${encodeURIComponent(attId)}`);
|
|
4193
|
+
return ok(`Attachment ${attId} removed from computer ${detachCid}.`);
|
|
4194
|
+
}
|
|
3082
4195
|
return err(`Unknown tool: ${name}`);
|
|
3083
4196
|
}
|
|
3084
4197
|
catch (e) {
|
|
@@ -3143,7 +4256,47 @@ async function runServer() {
|
|
|
3143
4256
|
result: {
|
|
3144
4257
|
protocolVersion: "2024-11-05",
|
|
3145
4258
|
capabilities: { tools: {} },
|
|
3146
|
-
serverInfo: { name: "miosa-mcp", version: "0.1.
|
|
4259
|
+
serverInfo: { name: "miosa-mcp", version: "0.1.2" },
|
|
4260
|
+
instructions: [
|
|
4261
|
+
"MIOSA cloud infrastructure — Firecracker microVMs you control via API.",
|
|
4262
|
+
"",
|
|
4263
|
+
"## Concepts",
|
|
4264
|
+
"- **Computer**: full Linux desktop VM (GUI, browser, apps). Use for visual tasks, browser automation, desktop control.",
|
|
4265
|
+
"- **Sandbox**: headless Linux VM. Use for code execution, builds, CI, scripts. No desktop.",
|
|
4266
|
+
"- **Deployment**: git-based app hosting with builds, releases, domains.",
|
|
4267
|
+
"- **Storage**: S3-compatible object storage (buckets + objects).",
|
|
4268
|
+
"- **Database**: managed Postgres, MySQL, or Redis.",
|
|
4269
|
+
"- **Volume**: persistent block storage, attachable to computers.",
|
|
4270
|
+
"",
|
|
4271
|
+
"## Core workflows",
|
|
4272
|
+
"",
|
|
4273
|
+
"### Run code (sandbox)",
|
|
4274
|
+
"create_sandbox → exec (or exec_python) → read output → destroy_sandbox",
|
|
4275
|
+
"",
|
|
4276
|
+
"### Desktop automation (computer)",
|
|
4277
|
+
"computer_create → computer_screenshot → computer_click/type/key → computer_screenshot → repeat",
|
|
4278
|
+
"",
|
|
4279
|
+
"### Deploy an app",
|
|
4280
|
+
"deployment_create(repo_url) → deployment_publish → custom_domain_add (optional)",
|
|
4281
|
+
"",
|
|
4282
|
+
"### Store files",
|
|
4283
|
+
"storage_bucket_create → storage_object_upload → storage_object_presign (for public URL)",
|
|
4284
|
+
"",
|
|
4285
|
+
"### Provision a database",
|
|
4286
|
+
'database_create(engine="postgres") → database_credentials → use connection string',
|
|
4287
|
+
"",
|
|
4288
|
+
"## Conventions",
|
|
4289
|
+
"- IDs: pass computer_id or sandbox_id to every tool that operates on a resource.",
|
|
4290
|
+
"- Sizes: xs (1cpu/2GB), small (2cpu/4GB), medium (4cpu/8GB), large (8cpu/16GB), xl (16cpu/32GB).",
|
|
4291
|
+
'- Status: "running" = ready. Poll with get/list until status is "running".',
|
|
4292
|
+
"- File paths inside VMs: /workspace is the default working directory. /home/user also writable.",
|
|
4293
|
+
"- exec timeout: default 30s. For installs (npm/pip), set timeout_ms=120000.",
|
|
4294
|
+
"- Screenshots: PNG bytes, 1024x768 default. Coordinates are absolute pixels from top-left (0,0).",
|
|
4295
|
+
"",
|
|
4296
|
+
"## Tool naming",
|
|
4297
|
+
"{resource}_{action} — e.g. computer_create, sandbox_exec, storage_bucket_list.",
|
|
4298
|
+
"Desktop tools: computer_screenshot, computer_click, computer_type, computer_key, computer_scroll.",
|
|
4299
|
+
].join("\n"),
|
|
3147
4300
|
},
|
|
3148
4301
|
});
|
|
3149
4302
|
break;
|
|
@@ -3215,17 +4368,19 @@ async function runDeviceFlow(endpoint, clientName) {
|
|
|
3215
4368
|
}
|
|
3216
4369
|
const flow = start.body;
|
|
3217
4370
|
console.log();
|
|
3218
|
-
console.log(
|
|
4371
|
+
console.log(` ${banner({ subtitle: "MCP install" })}`);
|
|
3219
4372
|
console.log();
|
|
3220
|
-
console.log(
|
|
3221
|
-
|
|
4373
|
+
console.log(kvPanel([
|
|
4374
|
+
{ label: "Open", value: chalk.cyan(flow.verification_uri_complete) },
|
|
4375
|
+
{ label: "Code", value: chalk.bold(flow.user_code) },
|
|
4376
|
+
]));
|
|
3222
4377
|
console.log();
|
|
3223
4378
|
try {
|
|
3224
4379
|
openUrl(flow.verification_uri_complete);
|
|
3225
|
-
console.log(chalk.dim("
|
|
4380
|
+
console.log(` ${icon.pending} ${chalk.dim("Browser opened. Waiting for approval…")}`);
|
|
3226
4381
|
}
|
|
3227
4382
|
catch {
|
|
3228
|
-
console.log(chalk.dim("
|
|
4383
|
+
console.log(` ${icon.warn} ${chalk.dim("Could not open a browser automatically.")}`);
|
|
3229
4384
|
}
|
|
3230
4385
|
const deadline = Date.now() + flow.expires_in * 1000;
|
|
3231
4386
|
const intervalMs = Math.max(flow.interval || 3, 1) * 1000;
|
|
@@ -3312,26 +4467,50 @@ function printManualSnippet(client, apiKey, remoteUrl) {
|
|
|
3312
4467
|
console.log(` ${chalk.cyan(`--header "Authorization: Bearer ${apiKey}"`)}`);
|
|
3313
4468
|
}
|
|
3314
4469
|
async function runInstall(opts) {
|
|
4470
|
+
const startTime = Date.now();
|
|
3315
4471
|
const config = loadConfig();
|
|
3316
4472
|
const clientName = `MIOSA MCP (${opts.client === "manual" ? "manual" : opts.client})`;
|
|
3317
|
-
console.log(chalk.bold("MIOSA MCP installer"), chalk.dim(`— wiring ${opts.client} → ${opts.remoteUrl}`));
|
|
3318
4473
|
const apiKey = await runDeviceFlow(config.endpoint, clientName);
|
|
3319
4474
|
if (opts.client === "claude") {
|
|
3320
4475
|
const wired = wireClaudeCode(apiKey, opts.remoteUrl, opts.scope);
|
|
3321
4476
|
if (wired.ok) {
|
|
3322
4477
|
console.log();
|
|
3323
|
-
console.log(
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
4478
|
+
console.log(kvPanel([
|
|
4479
|
+
{
|
|
4480
|
+
icon: icon.ok,
|
|
4481
|
+
label: "Server",
|
|
4482
|
+
value: chalk.bold(MCP_SERVER_NAME),
|
|
4483
|
+
},
|
|
4484
|
+
{
|
|
4485
|
+
icon: icon.ok,
|
|
4486
|
+
label: "Client",
|
|
4487
|
+
value: chalk.bold("Claude Code") + chalk.dim(` · ${opts.scope} scope`),
|
|
4488
|
+
},
|
|
4489
|
+
{
|
|
4490
|
+
icon: icon.ok,
|
|
4491
|
+
label: "Endpoint",
|
|
4492
|
+
value: chalk.dim(opts.remoteUrl),
|
|
4493
|
+
},
|
|
4494
|
+
]));
|
|
3327
4495
|
console.log();
|
|
3328
|
-
console.log(
|
|
3329
|
-
|
|
4496
|
+
console.log(hintBlock("Next", [
|
|
4497
|
+
"claude mcp list",
|
|
4498
|
+
"Try in Claude Code: 'Create a MIOSA sandbox and run python -c print(2+2)'",
|
|
4499
|
+
]));
|
|
4500
|
+
printElapsed(formatDuration(Date.now() - startTime));
|
|
3330
4501
|
return;
|
|
3331
4502
|
}
|
|
3332
4503
|
console.log();
|
|
3333
|
-
console.log(
|
|
3334
|
-
|
|
4504
|
+
console.log(errorEnvelope({
|
|
4505
|
+
title: "Could not auto-wire Claude Code",
|
|
4506
|
+
body: wired.reason,
|
|
4507
|
+
suggest: [
|
|
4508
|
+
"miosa mcp install # try again",
|
|
4509
|
+
"miosa mcp install --client manual # get the snippet instead",
|
|
4510
|
+
],
|
|
4511
|
+
withDebugHint: true,
|
|
4512
|
+
}));
|
|
4513
|
+
console.log();
|
|
3335
4514
|
printManualSnippet("claude", apiKey, opts.remoteUrl);
|
|
3336
4515
|
return;
|
|
3337
4516
|
}
|
|
@@ -3369,7 +4548,16 @@ export function register(program) {
|
|
|
3369
4548
|
}
|
|
3370
4549
|
catch (e) {
|
|
3371
4550
|
const msg = e instanceof Error ? e.message : String(e);
|
|
3372
|
-
console.
|
|
4551
|
+
console.log();
|
|
4552
|
+
console.log(errorEnvelope({
|
|
4553
|
+
title: "MCP install failed",
|
|
4554
|
+
body: msg,
|
|
4555
|
+
suggest: [
|
|
4556
|
+
"miosa mcp install # try again",
|
|
4557
|
+
"miosa login # re-authenticate first",
|
|
4558
|
+
],
|
|
4559
|
+
withDebugHint: true,
|
|
4560
|
+
}));
|
|
3373
4561
|
process.exit(3);
|
|
3374
4562
|
}
|
|
3375
4563
|
});
|