@ggui-ai/cli 0.2.0-alpha.4 → 0.3.0-rc.0
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 +38 -7
- package/dist/api-client.d.ts +42 -1
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +30 -2
- package/dist/auth-keys.d.ts +1 -1
- package/dist/auth-keys.d.ts.map +1 -1
- package/dist/auth-keys.js +22 -7
- package/dist/auth-store.d.ts +1 -1
- package/dist/auth-store.d.ts.map +1 -1
- package/dist/cli.js +112 -30
- package/dist/config-push.d.ts +79 -0
- package/dist/config-push.d.ts.map +1 -0
- package/dist/config-push.js +301 -0
- package/dist/create-app-command.d.ts +17 -0
- package/dist/create-app-command.d.ts.map +1 -0
- package/dist/create-app-command.js +58 -0
- package/dist/deploy-command.d.ts +67 -0
- package/dist/deploy-command.d.ts.map +1 -0
- package/dist/deploy-command.js +386 -0
- package/dist/dev-command.d.ts +5 -1
- package/dist/dev-command.d.ts.map +1 -1
- package/dist/dev-command.js +31 -1
- package/dist/export-pool-command.d.ts +25 -0
- package/dist/export-pool-command.d.ts.map +1 -0
- package/dist/export-pool-command.js +116 -0
- package/dist/filesystem-seed-pool-source.d.ts +13 -0
- package/dist/filesystem-seed-pool-source.d.ts.map +1 -0
- package/dist/filesystem-seed-pool-source.js +22 -0
- package/dist/generation-probe.d.ts +53 -7
- package/dist/generation-probe.d.ts.map +1 -1
- package/dist/generation-probe.js +37 -1
- package/dist/internal/artifact-install.d.ts +4 -2
- package/dist/internal/artifact-install.d.ts.map +1 -1
- package/dist/internal/artifact-install.js +11 -10
- package/dist/internal/artifact-publish.d.ts +34 -31
- package/dist/internal/artifact-publish.d.ts.map +1 -1
- package/dist/internal/artifact-publish.js +133 -241
- package/dist/internal/artifact-search.d.ts +2 -28
- package/dist/internal/artifact-search.d.ts.map +1 -1
- package/dist/internal/artifact-search.js +5 -4
- package/dist/internal/auth-strategy.d.ts +21 -31
- package/dist/internal/auth-strategy.d.ts.map +1 -1
- package/dist/internal/auth-strategy.js +15 -23
- package/dist/internal/register-author-key.d.ts +7 -1
- package/dist/internal/register-author-key.d.ts.map +1 -1
- package/dist/internal/register-author-key.js +56 -19
- package/dist/mcp-backend.d.ts +25 -25
- package/dist/mcp-backend.d.ts.map +1 -1
- package/dist/mcp-backend.js +65 -61
- package/dist/paths.d.ts +3 -3
- package/dist/paths.js +3 -3
- package/dist/persistent-secrets.js +1 -1
- package/dist/persistent-stores.d.ts +2 -2
- package/dist/persistent-stores.d.ts.map +1 -1
- package/dist/persistent-stores.js +5 -5
- package/dist/pool-artifact.d.ts +19 -0
- package/dist/pool-artifact.d.ts.map +1 -0
- package/dist/pool-artifact.js +59 -0
- package/dist/provider-key-command.d.ts +51 -0
- package/dist/provider-key-command.d.ts.map +1 -0
- package/dist/provider-key-command.js +222 -0
- package/dist/push-command.d.ts +47 -0
- package/dist/push-command.d.ts.map +1 -0
- package/dist/push-command.js +230 -0
- package/dist/serve-command.d.ts +23 -19
- package/dist/serve-command.d.ts.map +1 -1
- package/dist/serve-command.js +13 -26
- package/dist/theme-command.js +0 -2
- package/dist/tunnel-provider.d.ts +8 -7
- package/dist/tunnel-provider.d.ts.map +1 -1
- package/package.json +25 -21
- package/dist/internal/auth-cache.d.ts +0 -40
- package/dist/internal/auth-cache.d.ts.map +0 -1
- package/dist/internal/auth-cache.js +0 -133
- package/dist/persistent-short-code-index.d.ts +0 -14
- package/dist/persistent-short-code-index.d.ts.map +0 -1
- package/dist/persistent-short-code-index.js +0 -39
- package/dist/spawn-cloudflared.d.ts +0 -61
- package/dist/spawn-cloudflared.d.ts.map +0 -1
- package/dist/spawn-cloudflared.js +0 -167
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `spawnCloudflaredTunnel` — auto-tunnel helper for `ggui serve --public`.
|
|
3
|
-
*
|
|
4
|
-
* The local-dev → claude.ai integration loop has a well-known trap:
|
|
5
|
-
* every demo session would otherwise need to manually run
|
|
6
|
-
* `cloudflared tunnel --url http://localhost:6781`, grep the URL from
|
|
7
|
-
* its noisy stderr, then restart `ggui serve --public-base-url <url>`
|
|
8
|
-
* with that value pinned. `runtimeUrl` is auto-derived from
|
|
9
|
-
* `X-Forwarded-Host` and no longer depends on `publicBaseUrl`, but
|
|
10
|
-
* production deployments still need the URL pinned for OAuth callbacks
|
|
11
|
-
* and email magic-links. This helper closes the gap: one `--public`
|
|
12
|
-
* flag spawns cloudflared, parses its URL, and threads it into the
|
|
13
|
-
* boot config.
|
|
14
|
-
*
|
|
15
|
-
* **Trust posture.** This helper does NOT mask the public exposure. It
|
|
16
|
-
* is the operator's explicit opt-in (`--public`); when combined with
|
|
17
|
-
* `--dev-allow-all` the CLI front-end requires a second flag
|
|
18
|
-
* (`--i-know-its-public`) to prevent accidentally serving any-bearer
|
|
19
|
-
* auth over the internet.
|
|
20
|
-
*
|
|
21
|
-
* **Process management.** The spawned child is the operator's only
|
|
22
|
-
* dependency on the host's `cloudflared` binary; we don't bundle it.
|
|
23
|
-
* On `close()`, SIGTERM → SIGKILL fallback after 5s. AbortSignal
|
|
24
|
-
* support lets the CLI lifecycle (Ctrl-C handler in `runServe`)
|
|
25
|
-
* tear down cloudflared in lockstep with the server.
|
|
26
|
-
*/
|
|
27
|
-
import { spawn } from 'node:child_process';
|
|
28
|
-
/** Configured upper bound on how long we wait for cloudflared to
|
|
29
|
-
* print its public URL. Cold quick-tunnel typically takes 2-5s;
|
|
30
|
-
* setting too low surfaces a flaky "no URL detected" error to the
|
|
31
|
-
* operator before the tunnel actually fails. 30s is generous. */
|
|
32
|
-
const DEFAULT_URL_TIMEOUT_MS = 30_000;
|
|
33
|
-
/** Matcher for the trycloudflare URL inside cloudflared's stderr.
|
|
34
|
-
* cloudflared's exact log format has been stable since 2023; if it
|
|
35
|
-
* changes, this regex is the single point of impact. */
|
|
36
|
-
const TRYCLOUDFLARE_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
|
|
37
|
-
/**
|
|
38
|
-
* Spawn `cloudflared tunnel --url http://localhost:<port>` and resolve
|
|
39
|
-
* with the assigned trycloudflare URL. Rejects when:
|
|
40
|
-
*
|
|
41
|
-
* - The binary can't be spawned (cloudflared not installed / wrong
|
|
42
|
-
* path / permission denied). Error message includes the install
|
|
43
|
-
* hint.
|
|
44
|
-
* - cloudflared exits before printing a URL. Error includes any
|
|
45
|
-
* captured stderr tail.
|
|
46
|
-
* - URL detection times out. Error includes elapsed time + last
|
|
47
|
-
* ~10 lines of stderr so the operator can diagnose.
|
|
48
|
-
*/
|
|
49
|
-
export function spawnCloudflaredTunnel(opts) {
|
|
50
|
-
const binary = opts.binary ?? 'cloudflared';
|
|
51
|
-
const port = opts.port;
|
|
52
|
-
const timeout = opts.urlTimeoutMs ?? DEFAULT_URL_TIMEOUT_MS;
|
|
53
|
-
const log = opts.onLog ?? (() => { });
|
|
54
|
-
return new Promise((resolve, reject) => {
|
|
55
|
-
let child;
|
|
56
|
-
try {
|
|
57
|
-
child = spawn(binary, ['tunnel', '--url', `http://localhost:${port}`, '--no-autoupdate'], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
58
|
-
}
|
|
59
|
-
catch (err) {
|
|
60
|
-
reject(new Error(`Failed to spawn ${binary}: ${err.message}. ` +
|
|
61
|
-
`Install cloudflared from https://github.com/cloudflare/cloudflared/releases.`));
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
// Tail buffer for error context — cap so a chatty cloudflared
|
|
65
|
-
// version doesn't balloon memory.
|
|
66
|
-
const stderrTail = [];
|
|
67
|
-
const MAX_TAIL = 50;
|
|
68
|
-
let url;
|
|
69
|
-
let urlResolved = false;
|
|
70
|
-
let closed = false;
|
|
71
|
-
const startedAt = Date.now();
|
|
72
|
-
const timer = setTimeout(() => {
|
|
73
|
-
if (!urlResolved) {
|
|
74
|
-
reject(new Error(`cloudflared did not emit a tunnel URL within ${timeout}ms. ` +
|
|
75
|
-
`Last stderr lines:\n${stderrTail.slice(-10).join('\n')}`));
|
|
76
|
-
void close();
|
|
77
|
-
}
|
|
78
|
-
}, timeout);
|
|
79
|
-
function handleLine(line) {
|
|
80
|
-
log(line);
|
|
81
|
-
stderrTail.push(line);
|
|
82
|
-
if (stderrTail.length > MAX_TAIL) {
|
|
83
|
-
stderrTail.shift();
|
|
84
|
-
}
|
|
85
|
-
if (urlResolved)
|
|
86
|
-
return;
|
|
87
|
-
const match = line.match(TRYCLOUDFLARE_URL_RE);
|
|
88
|
-
if (match) {
|
|
89
|
-
url = match[0];
|
|
90
|
-
urlResolved = true;
|
|
91
|
-
clearTimeout(timer);
|
|
92
|
-
resolve({
|
|
93
|
-
url,
|
|
94
|
-
close,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
// cloudflared logs to BOTH stdout and stderr depending on version
|
|
99
|
-
// — wire both through the same line-handler so the regex catches
|
|
100
|
-
// wherever it lands.
|
|
101
|
-
function attachLineReader(stream) {
|
|
102
|
-
let buf = '';
|
|
103
|
-
stream.setEncoding('utf8');
|
|
104
|
-
stream.on('data', (chunk) => {
|
|
105
|
-
buf += chunk;
|
|
106
|
-
let nl = buf.indexOf('\n');
|
|
107
|
-
while (nl !== -1) {
|
|
108
|
-
const line = buf.slice(0, nl).trimEnd();
|
|
109
|
-
buf = buf.slice(nl + 1);
|
|
110
|
-
if (line.length > 0)
|
|
111
|
-
handleLine(line);
|
|
112
|
-
nl = buf.indexOf('\n');
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
if (child.stderr)
|
|
117
|
-
attachLineReader(child.stderr);
|
|
118
|
-
if (child.stdout)
|
|
119
|
-
attachLineReader(child.stdout);
|
|
120
|
-
child.on('error', (err) => {
|
|
121
|
-
clearTimeout(timer);
|
|
122
|
-
if (!urlResolved) {
|
|
123
|
-
reject(new Error(`cloudflared spawn error: ${err.message}. ` +
|
|
124
|
-
`Verify the binary is installed and executable.`));
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
child.on('exit', (code, sig) => {
|
|
128
|
-
clearTimeout(timer);
|
|
129
|
-
if (!urlResolved) {
|
|
130
|
-
reject(new Error(`cloudflared exited (code=${code}, signal=${sig}) before ` +
|
|
131
|
-
`emitting a tunnel URL (elapsed ${Date.now() - startedAt}ms). ` +
|
|
132
|
-
`Last stderr lines:\n${stderrTail.slice(-10).join('\n')}`));
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
function close() {
|
|
136
|
-
if (closed)
|
|
137
|
-
return Promise.resolve();
|
|
138
|
-
closed = true;
|
|
139
|
-
return new Promise((resolveClose) => {
|
|
140
|
-
if (child.killed || child.exitCode !== null) {
|
|
141
|
-
resolveClose();
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
const killTimer = setTimeout(() => {
|
|
145
|
-
if (!child.killed && child.exitCode === null) {
|
|
146
|
-
child.kill('SIGKILL');
|
|
147
|
-
}
|
|
148
|
-
}, 5000);
|
|
149
|
-
child.once('exit', () => {
|
|
150
|
-
clearTimeout(killTimer);
|
|
151
|
-
resolveClose();
|
|
152
|
-
});
|
|
153
|
-
child.kill('SIGTERM');
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
if (opts.signal) {
|
|
157
|
-
if (opts.signal.aborted) {
|
|
158
|
-
void close();
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
opts.signal.addEventListener('abort', () => void close(), {
|
|
162
|
-
once: true,
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
}
|