@marina-cloud/cli 0.0.2 → 0.0.4
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/README.md +15 -6
- package/dist/marina.mjs +216 -93
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,30 +2,39 @@
|
|
|
2
2
|
|
|
3
3
|
Deploy small internal apps to Marina Cloud.
|
|
4
4
|
|
|
5
|
+
Source code and Marina's deployment skill live in the
|
|
6
|
+
[marina-hq/marina](https://github.com/marina-hq/marina) repository.
|
|
7
|
+
|
|
5
8
|
```sh
|
|
6
9
|
npm install -g @marina-cloud/cli
|
|
7
10
|
marina setup
|
|
8
11
|
marina deploy
|
|
9
12
|
```
|
|
10
13
|
|
|
14
|
+
For a complete first run, including the deployment skill and demo app:
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npm install -g @marina-cloud/cli && marina setup --skills --deploy-demo --json
|
|
18
|
+
```
|
|
19
|
+
|
|
11
20
|
`marina setup` opens Clerk in the browser and delivers the resulting API key
|
|
12
|
-
directly back to the CLI. For unattended environments, `MARINA_TOKEN`
|
|
13
|
-
|
|
21
|
+
directly back to the CLI. For unattended environments, `MARINA_TOKEN` is the
|
|
22
|
+
process-only credential override.
|
|
14
23
|
|
|
15
24
|
The saved credential lives in `~/.marina/profile` with user-only permissions.
|
|
16
25
|
Use `marina profile` to inspect the active profile and `marina logout` to remove
|
|
17
26
|
its credential.
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
detected. Install or refresh it explicitly with:
|
|
28
|
+
Install Marina deployment skills with:
|
|
21
29
|
|
|
22
30
|
```sh
|
|
23
31
|
marina skills install
|
|
24
32
|
marina skills install --agent codex
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
The CLI
|
|
28
|
-
when a newer release is available.
|
|
35
|
+
The CLI reads release metadata from Marina's control plane and prints an
|
|
36
|
+
advisory update command at most once per day when a newer release is available.
|
|
37
|
+
It never updates itself.
|
|
29
38
|
|
|
30
39
|
## Agent-friendly output
|
|
31
40
|
|
package/dist/marina.mjs
CHANGED
|
@@ -16,14 +16,151 @@ import {
|
|
|
16
16
|
} from "node:fs";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
18
|
import { join } from "node:path";
|
|
19
|
+
|
|
20
|
+
// package.json
|
|
21
|
+
var package_default = {
|
|
22
|
+
name: "@marina-cloud/cli",
|
|
23
|
+
version: "0.0.4",
|
|
24
|
+
description: "Command-line client for Marina Cloud",
|
|
25
|
+
homepage: "https://github.com/marina-hq/marina#readme",
|
|
26
|
+
bugs: {
|
|
27
|
+
url: "https://github.com/marina-hq/marina/issues"
|
|
28
|
+
},
|
|
29
|
+
license: "Apache-2.0",
|
|
30
|
+
repository: {
|
|
31
|
+
type: "git",
|
|
32
|
+
url: "git+https://github.com/marina-hq/marina.git",
|
|
33
|
+
directory: "packages/cli"
|
|
34
|
+
},
|
|
35
|
+
bin: {
|
|
36
|
+
marina: "dist/marina.mjs"
|
|
37
|
+
},
|
|
38
|
+
files: [
|
|
39
|
+
"dist"
|
|
40
|
+
],
|
|
41
|
+
type: "module",
|
|
42
|
+
publishConfig: {
|
|
43
|
+
access: "public",
|
|
44
|
+
provenance: true
|
|
45
|
+
},
|
|
46
|
+
scripts: {
|
|
47
|
+
build: 'esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --loader:.md=text --loader:.yaml=text --loader:.html=text --loader:.css=text --loader:.txt=text --outfile=dist/marina.mjs --banner:js="#!/usr/bin/env node" && chmod +x dist/marina.mjs',
|
|
48
|
+
typecheck: "tsc --noEmit",
|
|
49
|
+
test: "pnpm build && node --experimental-strip-types --test src/*.test.ts",
|
|
50
|
+
prepack: "pnpm build"
|
|
51
|
+
},
|
|
52
|
+
devDependencies: {
|
|
53
|
+
"@types/node": "^26.2.0",
|
|
54
|
+
esbuild: "^0.25.0",
|
|
55
|
+
fflate: "^0.8.2",
|
|
56
|
+
typescript: "^5.9.0"
|
|
57
|
+
},
|
|
58
|
+
engines: {
|
|
59
|
+
node: ">=22"
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/identity.ts
|
|
64
|
+
var CLI_VERSION = package_default.version;
|
|
65
|
+
var cliRequestHeaders = () => ({
|
|
66
|
+
"user-agent": `marina-cli/${CLI_VERSION}`,
|
|
67
|
+
"x-marina-client": "cli",
|
|
68
|
+
"x-marina-client-version": CLI_VERSION
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// src/config.ts
|
|
19
72
|
var MARINA_HOME = process.env.MARINA_HOME ?? join(homedir(), ".marina");
|
|
20
73
|
var PROFILE = join(MARINA_HOME, "profile");
|
|
21
|
-
var
|
|
74
|
+
var PRODUCTION_CONTROL_PLANE = "https://marina.cloud";
|
|
75
|
+
var CONTROL_PLANE_CACHE_MS = 60 * 60 * 1e3;
|
|
76
|
+
var resolvedControlPlane = null;
|
|
77
|
+
function normalizeApiUrl(value) {
|
|
78
|
+
const url = new URL(value);
|
|
79
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
80
|
+
throw new Error("the Marina control plane must use http or https");
|
|
81
|
+
}
|
|
82
|
+
if (url.username || url.password || url.search || url.hash || url.pathname !== "/") {
|
|
83
|
+
throw new Error("the Marina control plane must be an origin without a path");
|
|
84
|
+
}
|
|
85
|
+
if (url.protocol === "http:" && url.hostname !== "localhost" && url.hostname !== "127.0.0.1" && url.hostname !== "[::1]") {
|
|
86
|
+
throw new Error("the Marina control plane must use https unless it is local");
|
|
87
|
+
}
|
|
88
|
+
return url.origin;
|
|
89
|
+
}
|
|
90
|
+
var controlPlaneHost = () => normalizeApiUrl(
|
|
91
|
+
process.env.MARINA_CONTROL_PLANE ?? readProfile().control_plane_host ?? PRODUCTION_CONTROL_PLANE
|
|
92
|
+
);
|
|
93
|
+
function cachedControlPlane(host = controlPlaneHost()) {
|
|
94
|
+
const cached = readProfile().control_plane;
|
|
95
|
+
if (!cached || cached.host !== host) return null;
|
|
96
|
+
try {
|
|
97
|
+
normalizeApiUrl(cached.api_url);
|
|
98
|
+
normalizeApiUrl(cached.dashboard_url);
|
|
99
|
+
return cached;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
var apiUrl = () => normalizeApiUrl(
|
|
105
|
+
process.env.MARINA_API ?? resolvedControlPlane?.api_url ?? cachedControlPlane()?.api_url ?? controlPlaneHost()
|
|
106
|
+
);
|
|
22
107
|
var dashboardUrl = () => {
|
|
23
|
-
if (process.env.MARINA_DASHBOARD) return process.env.MARINA_DASHBOARD
|
|
24
|
-
|
|
25
|
-
|
|
108
|
+
if (process.env.MARINA_DASHBOARD) return normalizeApiUrl(process.env.MARINA_DASHBOARD);
|
|
109
|
+
return normalizeApiUrl(
|
|
110
|
+
resolvedControlPlane?.dashboard_url ?? cachedControlPlane()?.dashboard_url ?? controlPlaneHost()
|
|
111
|
+
);
|
|
26
112
|
};
|
|
113
|
+
async function resolveControlPlane() {
|
|
114
|
+
const host = controlPlaneHost();
|
|
115
|
+
const cached = cachedControlPlane(host);
|
|
116
|
+
const checkedAt = cached ? Date.parse(cached.checked_at) : Number.NaN;
|
|
117
|
+
if (cached && Number.isFinite(checkedAt) && Date.now() - checkedAt < CONTROL_PLANE_CACHE_MS) {
|
|
118
|
+
resolvedControlPlane = {
|
|
119
|
+
api_url: cached.api_url,
|
|
120
|
+
dashboard_url: cached.dashboard_url,
|
|
121
|
+
latest_cli_version: cached.latest_cli_version
|
|
122
|
+
};
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (process.env.MARINA_DISABLE_CONTROL_PLANE_DISCOVERY === "1") return;
|
|
126
|
+
try {
|
|
127
|
+
const response = await fetch(`${host}/.well-known/marina`, {
|
|
128
|
+
headers: { ...cliRequestHeaders(), accept: "application/json" },
|
|
129
|
+
signal: AbortSignal.timeout(3e3)
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok)
|
|
132
|
+
throw new Error(`control-plane discovery failed (${String(response.status)})`);
|
|
133
|
+
const body = await response.json();
|
|
134
|
+
if (body.schema_version !== 1 || typeof body.api_url !== "string" || typeof body.dashboard_url !== "string") {
|
|
135
|
+
throw new Error("control-plane discovery returned an invalid response");
|
|
136
|
+
}
|
|
137
|
+
const discovered = {
|
|
138
|
+
api_url: normalizeApiUrl(body.api_url),
|
|
139
|
+
dashboard_url: normalizeApiUrl(body.dashboard_url),
|
|
140
|
+
latest_cli_version: typeof body.clients?.cli?.latest_version === "string" ? body.clients.cli.latest_version : null
|
|
141
|
+
};
|
|
142
|
+
resolvedControlPlane = discovered;
|
|
143
|
+
writeProfile({
|
|
144
|
+
...readProfile(),
|
|
145
|
+
control_plane: {
|
|
146
|
+
host,
|
|
147
|
+
checked_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
148
|
+
...discovered
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
} catch {
|
|
152
|
+
if (cached) {
|
|
153
|
+
resolvedControlPlane = {
|
|
154
|
+
api_url: cached.api_url,
|
|
155
|
+
dashboard_url: cached.dashboard_url,
|
|
156
|
+
latest_cli_version: cached.latest_cli_version
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function latestCliVersion() {
|
|
162
|
+
return resolvedControlPlane?.latest_cli_version ?? cachedControlPlane()?.latest_cli_version ?? null;
|
|
163
|
+
}
|
|
27
164
|
var profilePath = () => PROFILE;
|
|
28
165
|
function readProfile() {
|
|
29
166
|
try {
|
|
@@ -90,7 +227,7 @@ async function request(path, init) {
|
|
|
90
227
|
if (!token) throw new ApiError("unauthenticated", "not signed in \u2014 run `marina setup`", 401);
|
|
91
228
|
const res = await fetch(`${apiUrl()}${path}`, {
|
|
92
229
|
...init,
|
|
93
|
-
headers: { authorization: `Bearer ${token}`, ...init?.headers }
|
|
230
|
+
headers: { ...cliRequestHeaders(), authorization: `Bearer ${token}`, ...init?.headers }
|
|
94
231
|
});
|
|
95
232
|
const body = await res.json().catch(() => ({}));
|
|
96
233
|
if (!res.ok) {
|
|
@@ -107,7 +244,7 @@ async function exchangeCliLogin(code, codeVerifier) {
|
|
|
107
244
|
try {
|
|
108
245
|
res = await fetch(`${apiUrl()}/cli/auth/exchange`, {
|
|
109
246
|
method: "POST",
|
|
110
|
-
headers: { "content-type": "application/json" },
|
|
247
|
+
headers: { ...cliRequestHeaders(), "content-type": "application/json" },
|
|
111
248
|
body: JSON.stringify({ code, code_verifier: codeVerifier }),
|
|
112
249
|
signal: AbortSignal.timeout(LOGIN_EXCHANGE_TIMEOUT_MS)
|
|
113
250
|
});
|
|
@@ -147,9 +284,10 @@ async function startDeploy(zip, name, app) {
|
|
|
147
284
|
});
|
|
148
285
|
return res.deploy;
|
|
149
286
|
}
|
|
150
|
-
async function pollDeploy(id) {
|
|
287
|
+
async function pollDeploy(id, onProgress = () => void 0) {
|
|
151
288
|
for (; ; ) {
|
|
152
289
|
const { deploy: deploy2 } = await request(`/v1/deploys/${id}`);
|
|
290
|
+
onProgress(deploy2);
|
|
153
291
|
if (deploy2.status !== "queued" && deploy2.status !== "building") return deploy2;
|
|
154
292
|
await new Promise((resolve2) => setTimeout(resolve2, 500));
|
|
155
293
|
}
|
|
@@ -1564,6 +1702,28 @@ function say(line = "") {
|
|
|
1564
1702
|
function note(line) {
|
|
1565
1703
|
console.error(line);
|
|
1566
1704
|
}
|
|
1705
|
+
function createProgress() {
|
|
1706
|
+
let active = false;
|
|
1707
|
+
let lastPhase = null;
|
|
1708
|
+
let frame = 0;
|
|
1709
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1710
|
+
const interactive = process.stderr.isTTY === true && !json;
|
|
1711
|
+
return {
|
|
1712
|
+
update(phase, line) {
|
|
1713
|
+
if (interactive) {
|
|
1714
|
+
process.stderr.write(`\r\x1B[2K${frames[frame++ % frames.length]} ${line}`);
|
|
1715
|
+
active = true;
|
|
1716
|
+
} else if (phase !== lastPhase) {
|
|
1717
|
+
console.error(json ? JSON.stringify({ type: "progress", phase, message: line }) : line);
|
|
1718
|
+
}
|
|
1719
|
+
lastPhase = phase;
|
|
1720
|
+
},
|
|
1721
|
+
clear() {
|
|
1722
|
+
if (active) process.stderr.write("\r\x1B[2K");
|
|
1723
|
+
active = false;
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1567
1727
|
function result(payload) {
|
|
1568
1728
|
if (json) console.log(JSON.stringify({ schema_version: 1, ok: true, ...payload }, null, 2));
|
|
1569
1729
|
}
|
|
@@ -1746,52 +1906,8 @@ function installSkills(agent) {
|
|
|
1746
1906
|
return selected.map((target) => install(target, true));
|
|
1747
1907
|
}
|
|
1748
1908
|
|
|
1749
|
-
// package.json
|
|
1750
|
-
var package_default = {
|
|
1751
|
-
name: "@marina-cloud/cli",
|
|
1752
|
-
version: "0.0.2",
|
|
1753
|
-
description: "Command-line client for Marina Cloud",
|
|
1754
|
-
homepage: "https://github.com/marina-hq/marina#readme",
|
|
1755
|
-
bugs: {
|
|
1756
|
-
url: "https://github.com/marina-hq/marina/issues"
|
|
1757
|
-
},
|
|
1758
|
-
license: "Apache-2.0",
|
|
1759
|
-
repository: {
|
|
1760
|
-
type: "git",
|
|
1761
|
-
url: "git+https://github.com/marina-hq/marina.git",
|
|
1762
|
-
directory: "packages/cli"
|
|
1763
|
-
},
|
|
1764
|
-
bin: {
|
|
1765
|
-
marina: "dist/marina.mjs"
|
|
1766
|
-
},
|
|
1767
|
-
files: [
|
|
1768
|
-
"dist"
|
|
1769
|
-
],
|
|
1770
|
-
type: "module",
|
|
1771
|
-
publishConfig: {
|
|
1772
|
-
access: "public",
|
|
1773
|
-
provenance: true
|
|
1774
|
-
},
|
|
1775
|
-
scripts: {
|
|
1776
|
-
build: 'esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --loader:.md=text --loader:.yaml=text --loader:.html=text --loader:.css=text --loader:.txt=text --outfile=dist/marina.mjs --banner:js="#!/usr/bin/env node" && chmod +x dist/marina.mjs',
|
|
1777
|
-
typecheck: "tsc --noEmit",
|
|
1778
|
-
test: "pnpm build && node --experimental-strip-types --test src/*.test.ts",
|
|
1779
|
-
prepack: "pnpm build"
|
|
1780
|
-
},
|
|
1781
|
-
devDependencies: {
|
|
1782
|
-
"@types/node": "^26.2.0",
|
|
1783
|
-
esbuild: "^0.25.0",
|
|
1784
|
-
fflate: "^0.8.2",
|
|
1785
|
-
typescript: "^5.9.0"
|
|
1786
|
-
},
|
|
1787
|
-
engines: {
|
|
1788
|
-
node: ">=22"
|
|
1789
|
-
}
|
|
1790
|
-
};
|
|
1791
|
-
|
|
1792
1909
|
// src/update.ts
|
|
1793
|
-
var
|
|
1794
|
-
var LATEST_URL = "https://registry.npmjs.org/@marina-cloud%2Fcli/latest";
|
|
1910
|
+
var NAG_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
1795
1911
|
function isNewerVersion(candidate, current) {
|
|
1796
1912
|
const parse = (version) => version.replace(/^v/, "").split("-", 1)[0].split(".").map((part) => Number.parseInt(part, 10));
|
|
1797
1913
|
const left = parse(candidate);
|
|
@@ -1806,40 +1922,28 @@ function isNewerVersion(candidate, current) {
|
|
|
1806
1922
|
async function availableUpdate() {
|
|
1807
1923
|
if (process.env.MARINA_DISABLE_UPDATE_CHECK) return null;
|
|
1808
1924
|
const current = package_default.version;
|
|
1925
|
+
const latest = latestCliVersion();
|
|
1926
|
+
if (!latest || !isNewerVersion(latest, current)) return null;
|
|
1809
1927
|
const profile = readProfile();
|
|
1810
|
-
const
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
});
|
|
1818
|
-
if (response.ok) {
|
|
1819
|
-
const body = await response.json();
|
|
1820
|
-
if (body.version) {
|
|
1821
|
-
latest = body.version;
|
|
1822
|
-
writeProfile({
|
|
1823
|
-
...readProfile(),
|
|
1824
|
-
update: { checked_at: (/* @__PURE__ */ new Date()).toISOString(), latest }
|
|
1825
|
-
});
|
|
1826
|
-
}
|
|
1827
|
-
}
|
|
1828
|
-
} catch {
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
return latest && isNewerVersion(latest, current) ? {
|
|
1928
|
+
const notifiedAt = profile.update?.notified_at ? Date.parse(profile.update.notified_at) : Number.NaN;
|
|
1929
|
+
if (Number.isFinite(notifiedAt) && Date.now() - notifiedAt < NAG_INTERVAL_MS) return null;
|
|
1930
|
+
writeProfile({
|
|
1931
|
+
...profile,
|
|
1932
|
+
update: { latest, notified_at: (/* @__PURE__ */ new Date()).toISOString() }
|
|
1933
|
+
});
|
|
1934
|
+
return {
|
|
1832
1935
|
current,
|
|
1833
1936
|
latest,
|
|
1834
1937
|
command: "npm install -g @marina-cloud/cli@latest"
|
|
1835
|
-
}
|
|
1938
|
+
};
|
|
1836
1939
|
}
|
|
1837
1940
|
|
|
1838
1941
|
// src/index.ts
|
|
1839
1942
|
var HELP = `${bold("marina")} \u2014 deploy internal apps
|
|
1840
1943
|
|
|
1841
1944
|
marina setup sign in with Clerk in your browser
|
|
1842
|
-
--
|
|
1945
|
+
--skills install the Marina deployment skill
|
|
1946
|
+
--deploy-demo deploy Hello Marina after signing in
|
|
1843
1947
|
marina logout remove the saved credential
|
|
1844
1948
|
marina profile show where this CLI is signed in
|
|
1845
1949
|
marina skills install install or update the Marina deployment skill
|
|
@@ -1858,8 +1962,7 @@ var HELP = `${bold("marina")} \u2014 deploy internal apps
|
|
|
1858
1962
|
marina list apps in your workspace
|
|
1859
1963
|
marina open open this project's app
|
|
1860
1964
|
|
|
1861
|
-
${dim("--json machine-readable result on stdout, progress on stderr")}
|
|
1862
|
-
${dim(`API: ${apiUrl()} (override with MARINA_API)`)}`;
|
|
1965
|
+
${dim("--json machine-readable result on stdout, progress on stderr")}`;
|
|
1863
1966
|
var ANSI_ESCAPE = new RegExp(`${String.fromCodePoint(27)}\\[[0-9;]*m`, "g");
|
|
1864
1967
|
var shouldCheckForUpdates = false;
|
|
1865
1968
|
function targetApp(flag) {
|
|
@@ -1872,7 +1975,7 @@ function targetApp(flag) {
|
|
|
1872
1975
|
return app;
|
|
1873
1976
|
}
|
|
1874
1977
|
var shortDigest = (digest) => digest?.slice(7, 14) ?? "\u2014";
|
|
1875
|
-
async function deploy(dirArg, flags) {
|
|
1978
|
+
async function deploy(dirArg, flags, emitResult = true) {
|
|
1876
1979
|
let dir = null;
|
|
1877
1980
|
let link = null;
|
|
1878
1981
|
let target;
|
|
@@ -1894,13 +1997,14 @@ async function deploy(dirArg, flags) {
|
|
|
1894
1997
|
packed = pack(dir);
|
|
1895
1998
|
}
|
|
1896
1999
|
for (const secret of packed.skippedSecrets) say(dim(`skipped ${secret} \u2014 secrets stay local`));
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
);
|
|
2000
|
+
const progress = createProgress();
|
|
2001
|
+
const size = `${String(packed.fileCount)} files, ${String(Math.round(packed.totalBytes / 1024))} KB`;
|
|
2002
|
+
progress.update("uploading", `Uploading ${name} (${size})`);
|
|
1900
2003
|
let started;
|
|
1901
2004
|
try {
|
|
1902
2005
|
started = await startDeploy(packed.zip, name, target);
|
|
1903
2006
|
} catch (error) {
|
|
2007
|
+
progress.clear();
|
|
1904
2008
|
if (error instanceof ApiError && error.code === "not_found" && link) {
|
|
1905
2009
|
failure(
|
|
1906
2010
|
"link_stale",
|
|
@@ -1910,7 +2014,18 @@ async function deploy(dirArg, flags) {
|
|
|
1910
2014
|
}
|
|
1911
2015
|
throw error;
|
|
1912
2016
|
}
|
|
1913
|
-
const
|
|
2017
|
+
const deployedAt = Date.now();
|
|
2018
|
+
let deployed;
|
|
2019
|
+
try {
|
|
2020
|
+
deployed = await pollDeploy(started.id, (current) => {
|
|
2021
|
+
if (current.status !== "queued" && current.status !== "building") return;
|
|
2022
|
+
const elapsed = Math.max(1, Math.round((Date.now() - deployedAt) / 1e3));
|
|
2023
|
+
const message = current.status === "queued" ? `Waiting for a deploy worker (${String(elapsed)}s)` : `Building and verifying (${String(elapsed)}s)`;
|
|
2024
|
+
progress.update(current.status, message);
|
|
2025
|
+
});
|
|
2026
|
+
} finally {
|
|
2027
|
+
progress.clear();
|
|
2028
|
+
}
|
|
1914
2029
|
if (deployed.status === "refused" && deployed.refusal) {
|
|
1915
2030
|
say(`${red("refused")} ${deployed.refusal.message}`);
|
|
1916
2031
|
if (deployed.refusal.action) say(` ${deployed.refusal.action}`);
|
|
@@ -1949,7 +2064,7 @@ async function deploy(dirArg, flags) {
|
|
|
1949
2064
|
if (deployed.version_url) say(` try this version: ${deployed.version_url}`);
|
|
1950
2065
|
say(dim(" a publisher can approve it from the Marina inbox"));
|
|
1951
2066
|
}
|
|
1952
|
-
|
|
2067
|
+
const payload = {
|
|
1953
2068
|
command: "deploy",
|
|
1954
2069
|
status: "succeeded",
|
|
1955
2070
|
app: slug,
|
|
@@ -1960,7 +2075,9 @@ async function deploy(dirArg, flags) {
|
|
|
1960
2075
|
preparation: deployed.preparation,
|
|
1961
2076
|
live: published,
|
|
1962
2077
|
demo: dirArg === "demo"
|
|
1963
|
-
}
|
|
2078
|
+
};
|
|
2079
|
+
if (emitResult) result(payload);
|
|
2080
|
+
return payload;
|
|
1964
2081
|
}
|
|
1965
2082
|
async function status(appFlag) {
|
|
1966
2083
|
const app = targetApp(appFlag);
|
|
@@ -2076,12 +2193,13 @@ async function main() {
|
|
|
2076
2193
|
const { positionals, values } = parseArgs({
|
|
2077
2194
|
allowPositionals: true,
|
|
2078
2195
|
options: {
|
|
2079
|
-
token: { type: "string" },
|
|
2080
2196
|
name: { type: "string" },
|
|
2081
2197
|
app: { type: "string" },
|
|
2082
2198
|
to: { type: "string" },
|
|
2083
2199
|
plain: { type: "boolean" },
|
|
2084
2200
|
agent: { type: "string" },
|
|
2201
|
+
skills: { type: "boolean" },
|
|
2202
|
+
"deploy-demo": { type: "boolean" },
|
|
2085
2203
|
json: { type: "boolean" },
|
|
2086
2204
|
help: { type: "boolean", short: "h" }
|
|
2087
2205
|
}
|
|
@@ -2093,14 +2211,12 @@ async function main() {
|
|
|
2093
2211
|
result({ command: "help", usage: HELP.replaceAll(ANSI_ESCAPE, "") });
|
|
2094
2212
|
return;
|
|
2095
2213
|
}
|
|
2214
|
+
await resolveControlPlane();
|
|
2096
2215
|
shouldCheckForUpdates = true;
|
|
2097
2216
|
switch (command) {
|
|
2098
2217
|
case "setup":
|
|
2099
2218
|
case "login": {
|
|
2100
|
-
|
|
2101
|
-
if (token) {
|
|
2102
|
-
saveToken(token);
|
|
2103
|
-
} else {
|
|
2219
|
+
if (!process.env.MARINA_TOKEN) {
|
|
2104
2220
|
let lastReportedRemaining = -1;
|
|
2105
2221
|
const signedIn = await loginWithBrowser(
|
|
2106
2222
|
(url) => {
|
|
@@ -2131,11 +2247,18 @@ async function main() {
|
|
|
2131
2247
|
saveToken(signedIn.token);
|
|
2132
2248
|
}
|
|
2133
2249
|
say(`${green("ok")} signed in ${dim(`(${apiUrl()})`)}`);
|
|
2250
|
+
const installed = values.skills ? installSkills(values.agent) : void 0;
|
|
2251
|
+
for (const skill of installed ?? []) {
|
|
2252
|
+
say(`${green("ok")} ${skill.status} Marina skill for ${skill.agent} ${dim(skill.path)}`);
|
|
2253
|
+
}
|
|
2254
|
+
const demo = values["deploy-demo"] ? await deploy("demo", { name: values.name }, false) : void 0;
|
|
2134
2255
|
result({
|
|
2135
2256
|
command: "setup",
|
|
2136
2257
|
signed_in: true,
|
|
2137
2258
|
api: apiUrl(),
|
|
2138
|
-
profile: profilePath()
|
|
2259
|
+
profile: profilePath(),
|
|
2260
|
+
...installed ? { skills: installed } : {},
|
|
2261
|
+
...demo ? { deploy: demo } : {}
|
|
2139
2262
|
});
|
|
2140
2263
|
return;
|
|
2141
2264
|
}
|
|
@@ -2231,8 +2354,8 @@ async function run() {
|
|
|
2231
2354
|
if (!shouldCheckForUpdates) return;
|
|
2232
2355
|
const update = await availableUpdate();
|
|
2233
2356
|
if (update) {
|
|
2234
|
-
const
|
|
2235
|
-
note(isJsonMode() ?
|
|
2357
|
+
const detail = `Marina CLI ${update.latest} is available (current ${update.current}) \u2014 ${update.command}`;
|
|
2358
|
+
note(isJsonMode() ? detail : `${bold("Update available:")} ${detail}`);
|
|
2236
2359
|
}
|
|
2237
2360
|
}
|
|
2238
2361
|
run().catch((error) => {
|