@cloudgrid-io/mcp 0.20.29 → 0.20.30
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 +1 -1
- package/src/tools/deploy.js +48 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudgrid-io/mcp",
|
|
3
|
-
"version": "0.20.
|
|
3
|
+
"version": "0.20.30",
|
|
4
4
|
"description": "MCP access to CloudGrid — the live runtime environment where the user's apps run WITH the infrastructure they need out of the box: managed MongoDB, Redis (cache/queues/pub-sub), a pgvector store, persistent disk, and built-in AI with no API keys, in any language or stack, no Docker or Kubernetes to manage. Brainstorm, build, plug, and improve apps, games, dashboards, and tools; instant single-page publishes and full runtime-app deploys with a live URL — for developers and non-technical users. Three editions: local stdio (full toolset), hosted web (light, CLI-free), and a public docs-search server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/tools/deploy.js
CHANGED
|
@@ -1424,9 +1424,10 @@ export async function runPlug(ctx, input, deps = {}) {
|
|
|
1424
1424
|
// budget. Fast builds come back "live" in this same tool call; a build that
|
|
1425
1425
|
// outlives the budget returns "building" with explicit do-not-claim-live copy;
|
|
1426
1426
|
// a failed build surfaces the platform's user-language error instead of a URL.
|
|
1427
|
-
if ((data.status === "building" || data.poll_url) && data.poll_url
|
|
1427
|
+
if ((data.status === "building" || data.poll_url) && !useAnonWire && (data.poll_url || data.entity_id)) {
|
|
1428
1428
|
const verdict = await pollDeployTrace(ctx, {
|
|
1429
1429
|
pollUrl: data.poll_url,
|
|
1430
|
+
entityId: data.entity_id,
|
|
1430
1431
|
grid: data.grid,
|
|
1431
1432
|
budgetMs: ctx.deployPollBudgetMs ?? DEPLOY_POLL_BUDGET_MS,
|
|
1432
1433
|
intervalMs: ctx.deployPollIntervalMs ?? DEPLOY_POLL_INTERVAL_MS,
|
|
@@ -1773,6 +1774,34 @@ const sleepMs = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
1773
1774
|
// GET one deploy-trace snapshot. Returns { status, error } — status is the
|
|
1774
1775
|
// server's word ("queued"/"building"/"success"/"failed"), error is the
|
|
1775
1776
|
// user-language message when failed. Throws only on a non-OK HTTP response.
|
|
1777
|
+
// Resolve a pollable deploy target. The hosted inline-create 202 returns
|
|
1778
|
+
// poll_url:null and trace_id:null (the trace is minted async), so when we only
|
|
1779
|
+
// have an entity_id we look up its latest deploy — GET /entities/:id/deploys
|
|
1780
|
+
// exposes a real trace_id even when the plug response omitted it — and poll the
|
|
1781
|
+
// normal /deploys/<trace_id>. Returns a poll path, or null when there is no
|
|
1782
|
+
// trace yet (the caller retries within its budget). Never throws.
|
|
1783
|
+
async function resolvePollUrl(ctx, { pollUrl, entityId, grid }) {
|
|
1784
|
+
if (pollUrl) return pollUrl;
|
|
1785
|
+
if (!entityId) return null;
|
|
1786
|
+
try {
|
|
1787
|
+
const token = await ctx.getToken();
|
|
1788
|
+
const headers = {};
|
|
1789
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
1790
|
+
const orgSlug = grid || (await ctx.getActiveGrid());
|
|
1791
|
+
if (orgSlug) {
|
|
1792
|
+
headers["X-CloudGrid-Grid"] = orgSlug;
|
|
1793
|
+
headers["X-CloudGrid-Org"] = orgSlug;
|
|
1794
|
+
}
|
|
1795
|
+
const res = await fetch(`${API_BASE}/api/v2/entities/${encodeURIComponent(entityId)}/deploys?limit=1`, { headers });
|
|
1796
|
+
if (!res.ok) return null;
|
|
1797
|
+
const data = await res.json();
|
|
1798
|
+
const traceId = data?.deploys?.[0]?.trace_id;
|
|
1799
|
+
return traceId ? `/deploys/${traceId}` : null;
|
|
1800
|
+
} catch {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1776
1805
|
async function fetchDeployTrace(ctx, { pollUrl, grid }) {
|
|
1777
1806
|
const token = await ctx.getToken();
|
|
1778
1807
|
const headers = {};
|
|
@@ -1799,12 +1828,16 @@ async function fetchDeployTrace(ctx, { pollUrl, grid }) {
|
|
|
1799
1828
|
// Poll until terminal or the budget runs out. Never throws — an unreachable
|
|
1800
1829
|
// poll endpoint degrades to { status: "unknown" } and the caller keeps the
|
|
1801
1830
|
// do-not-claim-live wording (the safe direction).
|
|
1802
|
-
export async function pollDeployTrace(ctx, { pollUrl, grid, budgetMs = DEPLOY_POLL_BUDGET_MS, intervalMs = DEPLOY_POLL_INTERVAL_MS }) {
|
|
1831
|
+
export async function pollDeployTrace(ctx, { pollUrl, entityId, grid, budgetMs = DEPLOY_POLL_BUDGET_MS, intervalMs = DEPLOY_POLL_INTERVAL_MS }) {
|
|
1803
1832
|
const start = Date.now();
|
|
1804
1833
|
let last = { status: "unknown", error: null };
|
|
1805
1834
|
for (;;) {
|
|
1806
1835
|
try {
|
|
1807
|
-
|
|
1836
|
+
// Resolve each iteration: on the hosted inline-create path poll_url is
|
|
1837
|
+
// null and the trace may not exist for the first second or two, so keep
|
|
1838
|
+
// trying to resolve it from the entity until it appears.
|
|
1839
|
+
const target = await resolvePollUrl(ctx, { pollUrl, entityId, grid });
|
|
1840
|
+
if (target) last = await fetchDeployTrace(ctx, { pollUrl: target, grid });
|
|
1808
1841
|
} catch {
|
|
1809
1842
|
// transient/unauthorized/network — keep last, retry within budget
|
|
1810
1843
|
}
|
|
@@ -1819,13 +1852,21 @@ export async function pollDeployTrace(ctx, { pollUrl, grid, budgetMs = DEPLOY_PO
|
|
|
1819
1852
|
// were missing — they had no way to confirm a runtime build came live and
|
|
1820
1853
|
// blind-polled the public URL into 502s.
|
|
1821
1854
|
export async function runCheckDeploy(ctx, { poll_url, grid } = {}) {
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1855
|
+
const gridSlug = grid || ctx.state.lastDrop?.grid;
|
|
1856
|
+
const entityId = ctx.state.lastDrop?.entity_id;
|
|
1857
|
+
// poll_url may be null on the hosted inline-create path — fall back to the
|
|
1858
|
+
// session's entity_id and resolve the trace from /entities/:id/deploys.
|
|
1859
|
+
const target = await resolvePollUrl(ctx, {
|
|
1860
|
+
pollUrl: poll_url || ctx.state.lastDrop?.poll_url,
|
|
1861
|
+
entityId,
|
|
1862
|
+
grid: gridSlug,
|
|
1863
|
+
});
|
|
1864
|
+
if (!target) {
|
|
1824
1865
|
throw new Error(
|
|
1825
|
-
"No build to check. Pass poll_url from a grid_deploy result, or deploy something first in this session. (Instant inspiration deploys are live on return and have no
|
|
1866
|
+
"No build to check. Pass poll_url from a grid_deploy result, or deploy something first in this session. (Instant inspiration deploys are live on return and have no build to poll.)",
|
|
1826
1867
|
);
|
|
1827
1868
|
}
|
|
1828
|
-
const verdict = await fetchDeployTrace(ctx, { pollUrl, grid:
|
|
1869
|
+
const verdict = await fetchDeployTrace(ctx, { pollUrl: target, grid: gridSlug });
|
|
1829
1870
|
const url = ctx.state.lastDrop?.url;
|
|
1830
1871
|
if (verdict.status === "success") {
|
|
1831
1872
|
return {
|