sparkecoder 0.1.102 → 0.1.103
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/cli.js +226 -2
- package/dist/cli.js.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +5 -1
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/package-lock.json +7 -7
- /package/web/.next/standalone/web/.next/static/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_ssgManifest.js +0 -0
- /package/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_buildManifest.js +0 -0
- /package/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{wV0cf4IbCdyxPIPIWZqMD → zSDiLDq3NGRZ_Ys5zvnML}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -11575,6 +11575,83 @@ var init_scheduler = __esm({
|
|
|
11575
11575
|
}
|
|
11576
11576
|
});
|
|
11577
11577
|
|
|
11578
|
+
// src/cloudflare/api.ts
|
|
11579
|
+
var api_exports = {};
|
|
11580
|
+
__export(api_exports, {
|
|
11581
|
+
createRemoteTunnel: () => createRemoteTunnel,
|
|
11582
|
+
setTunnelConfig: () => setTunnelConfig,
|
|
11583
|
+
upsertDnsCname: () => upsertDnsCname
|
|
11584
|
+
});
|
|
11585
|
+
async function cf(cfg, method, path, body) {
|
|
11586
|
+
const res = await fetch(`${CF_API}${path}`, {
|
|
11587
|
+
method,
|
|
11588
|
+
headers: {
|
|
11589
|
+
Authorization: `Bearer ${cfg.apiToken}`,
|
|
11590
|
+
"Content-Type": "application/json"
|
|
11591
|
+
},
|
|
11592
|
+
body: body == null ? void 0 : JSON.stringify(body)
|
|
11593
|
+
});
|
|
11594
|
+
const json = await res.json().catch(() => ({}));
|
|
11595
|
+
if (!res.ok || json?.success === false) {
|
|
11596
|
+
const errMsg = json?.errors?.map((e) => `${e.code}: ${e.message}`).join("; ");
|
|
11597
|
+
throw new Error(
|
|
11598
|
+
`Cloudflare API ${method} ${path} failed (HTTP ${res.status}): ${errMsg ?? JSON.stringify(json).slice(0, 400)}`
|
|
11599
|
+
);
|
|
11600
|
+
}
|
|
11601
|
+
return json.result ?? json;
|
|
11602
|
+
}
|
|
11603
|
+
async function createRemoteTunnel(cfg, name) {
|
|
11604
|
+
const created = await cf(
|
|
11605
|
+
cfg,
|
|
11606
|
+
"POST",
|
|
11607
|
+
`/accounts/${cfg.accountId}/cfd_tunnel`,
|
|
11608
|
+
{ name, config_src: "cloudflare" }
|
|
11609
|
+
);
|
|
11610
|
+
let token = created.token;
|
|
11611
|
+
if (!token) {
|
|
11612
|
+
token = await cf(cfg, "GET", `/accounts/${cfg.accountId}/cfd_tunnel/${created.id}/token`);
|
|
11613
|
+
}
|
|
11614
|
+
return { id: created.id, name: created.name, token };
|
|
11615
|
+
}
|
|
11616
|
+
async function setTunnelConfig(cfg, tunnelId, ingress) {
|
|
11617
|
+
await cf(cfg, "PUT", `/accounts/${cfg.accountId}/cfd_tunnel/${tunnelId}/configurations`, {
|
|
11618
|
+
config: { ingress: [...ingress, { service: "http_status:404" }] }
|
|
11619
|
+
});
|
|
11620
|
+
}
|
|
11621
|
+
async function upsertDnsCname(cfg, hostname, tunnelId) {
|
|
11622
|
+
if (!cfg.zoneId) throw new Error("zoneId required to manage DNS");
|
|
11623
|
+
const target = `${tunnelId}.cfargotunnel.com`;
|
|
11624
|
+
const existing = await cf(
|
|
11625
|
+
cfg,
|
|
11626
|
+
"GET",
|
|
11627
|
+
`/zones/${cfg.zoneId}/dns_records?name=${encodeURIComponent(hostname)}`
|
|
11628
|
+
);
|
|
11629
|
+
if (Array.isArray(existing) && existing.length > 0) {
|
|
11630
|
+
await cf(cfg, "PUT", `/zones/${cfg.zoneId}/dns_records/${existing[0].id}`, {
|
|
11631
|
+
type: "CNAME",
|
|
11632
|
+
name: hostname,
|
|
11633
|
+
content: target,
|
|
11634
|
+
proxied: true,
|
|
11635
|
+
ttl: 1
|
|
11636
|
+
});
|
|
11637
|
+
} else {
|
|
11638
|
+
await cf(cfg, "POST", `/zones/${cfg.zoneId}/dns_records`, {
|
|
11639
|
+
type: "CNAME",
|
|
11640
|
+
name: hostname,
|
|
11641
|
+
content: target,
|
|
11642
|
+
proxied: true,
|
|
11643
|
+
ttl: 1
|
|
11644
|
+
});
|
|
11645
|
+
}
|
|
11646
|
+
}
|
|
11647
|
+
var CF_API;
|
|
11648
|
+
var init_api = __esm({
|
|
11649
|
+
"src/cloudflare/api.ts"() {
|
|
11650
|
+
"use strict";
|
|
11651
|
+
CF_API = "https://api.cloudflare.com/client/v4";
|
|
11652
|
+
}
|
|
11653
|
+
});
|
|
11654
|
+
|
|
11578
11655
|
// src/cli.ts
|
|
11579
11656
|
import { Command } from "commander";
|
|
11580
11657
|
import chalk from "chalk";
|
|
@@ -13104,7 +13181,11 @@ ${prompt}` });
|
|
|
13104
13181
|
await writeSSE(JSON.stringify({ type: "reasoning-end", id: reasoningId }));
|
|
13105
13182
|
}
|
|
13106
13183
|
if (!isAborted) {
|
|
13107
|
-
|
|
13184
|
+
try {
|
|
13185
|
+
await result.saveResponseMessages();
|
|
13186
|
+
} catch (err) {
|
|
13187
|
+
console.error("saveResponseMessages failed (continuing):", err?.message ?? err);
|
|
13188
|
+
}
|
|
13108
13189
|
}
|
|
13109
13190
|
if (isAborted) {
|
|
13110
13191
|
await writeSSE(JSON.stringify({ type: "abort" }));
|
|
@@ -16828,7 +16909,7 @@ program.command("slack-setup").description("Interactively configure Slack integr
|
|
|
16828
16909
|
process.exit(1);
|
|
16829
16910
|
}
|
|
16830
16911
|
});
|
|
16831
|
-
program.command("cloudflared-setup").description("Auto-detect cloudflared + set up a tunnel to this sparkecoder (interactive)").option("--port <port>", "Local sparkecoder web UI port", "6969").option("--api-port <port>", "Local sparkecoder API port", "3141").option("--hostname <host>", "Public hostname you want to assign (e.g. sf-mac-1.example.com)").option("--tunnel-name <name>", "Tunnel name to reuse or create", "sparkecoder").option("--team <team>", "Your Cloudflare Access team subdomain (e.g. studyfetch -> studyfetch.cloudflareaccess.com)").option("--allowed-emails <emails>", "Comma-separated allow-listed emails").option("-y, --yes", "Skip confirmations and do everything non-interactively", false).option("--print-only", "Skip auto-setup and just print the copy/paste recipe", false).action(async (options) => {
|
|
16912
|
+
program.command("cloudflared-setup").description("Auto-detect cloudflared + set up a tunnel to this sparkecoder (interactive)").option("--port <port>", "Local sparkecoder web UI port", "6969").option("--api-port <port>", "Local sparkecoder API port", "3141").option("--hostname <host>", "Public hostname you want to assign (e.g. sf-mac-1.example.com)").option("--tunnel-name <name>", "Tunnel name to reuse or create", "sparkecoder").option("--team <team>", "Your Cloudflare Access team subdomain (e.g. studyfetch -> studyfetch.cloudflareaccess.com)").option("--allowed-emails <emails>", "Comma-separated allow-listed emails").option("-y, --yes", "Skip confirmations and do everything non-interactively", false).option("--print-only", "Skip auto-setup and just print the copy/paste recipe", false).option("--cf-api-token <token>", "Cloudflare API token (Account: Cloudflare Tunnel: Edit + Zone: DNS: Edit). Or set CF_API_TOKEN env").option("--cf-account-id <id>", "Cloudflare account ID. Or set CF_ACCOUNT_ID env").option("--cf-zone-id <id>", "Cloudflare zone ID for the hostname. Or set CF_ZONE_ID env").option("--remote", "Provision the tunnel via the remote sparkecoder server (server holds the Cloudflare credentials)", false).option("--setup-secret <secret>", "Tunnel setup secret (required when server has TUNNEL_SETUP_SECRET set). Or set SPARKECODER_TUNNEL_SECRET env").action(async (options) => {
|
|
16832
16913
|
const { execSync: execSync2, spawnSync } = await import("child_process");
|
|
16833
16914
|
const { homedir: homedir2 } = await import("os");
|
|
16834
16915
|
const { copyFileSync } = await import("fs");
|
|
@@ -16838,6 +16919,149 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
16838
16919
|
const team = options.team || "<your-team>";
|
|
16839
16920
|
const emails = (options.allowedEmails || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
16840
16921
|
const yes = !!options.yes;
|
|
16922
|
+
const ensureCloudflared = () => {
|
|
16923
|
+
try {
|
|
16924
|
+
return execSync2("command -v cloudflared", { stdio: ["ignore", "pipe", "ignore"] }).toString().trim() || null;
|
|
16925
|
+
} catch {
|
|
16926
|
+
return null;
|
|
16927
|
+
}
|
|
16928
|
+
};
|
|
16929
|
+
const installCloudflaredIfNeeded = async () => {
|
|
16930
|
+
let cfPath = ensureCloudflared();
|
|
16931
|
+
if (cfPath) return cfPath;
|
|
16932
|
+
console.log(chalk.yellow("cloudflared is not installed."));
|
|
16933
|
+
if (process.platform === "darwin") {
|
|
16934
|
+
try {
|
|
16935
|
+
execSync2("command -v brew", { stdio: "ignore" });
|
|
16936
|
+
} catch {
|
|
16937
|
+
return null;
|
|
16938
|
+
}
|
|
16939
|
+
console.log(chalk.dim("Installing via `brew install cloudflared`..."));
|
|
16940
|
+
spawnSync("brew", ["install", "cloudflared"], { stdio: "inherit" });
|
|
16941
|
+
cfPath = ensureCloudflared();
|
|
16942
|
+
}
|
|
16943
|
+
return cfPath;
|
|
16944
|
+
};
|
|
16945
|
+
const installAsService = (token) => {
|
|
16946
|
+
let r = spawnSync("cloudflared", ["service", "install", token], { stdio: "inherit" });
|
|
16947
|
+
if (r.status !== 0 && process.platform !== "win32") {
|
|
16948
|
+
console.log(chalk.dim("Retrying with sudo..."));
|
|
16949
|
+
r = spawnSync("sudo", ["cloudflared", "service", "install", token], { stdio: "inherit" });
|
|
16950
|
+
}
|
|
16951
|
+
return r.status === 0;
|
|
16952
|
+
};
|
|
16953
|
+
if (options.remote) {
|
|
16954
|
+
try {
|
|
16955
|
+
let config = loadConfig(options.config, process.cwd());
|
|
16956
|
+
let remoteUrl = config.resolvedRemoteServer.url;
|
|
16957
|
+
if (!remoteUrl) {
|
|
16958
|
+
console.log(chalk.red("No remoteServer.url configured. Run `sparkecoder login` (or set remoteServer.url in your config) first."));
|
|
16959
|
+
process.exitCode = 1;
|
|
16960
|
+
return;
|
|
16961
|
+
}
|
|
16962
|
+
let authKey3 = config.resolvedRemoteServer.authKey;
|
|
16963
|
+
if (!authKey3) {
|
|
16964
|
+
authKey3 = await ensureRemoteAuthKey(remoteUrl);
|
|
16965
|
+
}
|
|
16966
|
+
const setupSecret = options.setupSecret || process.env.SPARKECODER_TUNNEL_SECRET;
|
|
16967
|
+
const hostname = options.hostname;
|
|
16968
|
+
console.log(chalk.bold(
|
|
16969
|
+
hostname ? `Requesting tunnel for ${hostname} from remote server...` : "Requesting auto-generated tunnel from remote server..."
|
|
16970
|
+
));
|
|
16971
|
+
const reqBody = {
|
|
16972
|
+
port: Number(apiPort),
|
|
16973
|
+
webPort: Number(port),
|
|
16974
|
+
name: tunnelName
|
|
16975
|
+
};
|
|
16976
|
+
if (hostname) reqBody.hostname = hostname;
|
|
16977
|
+
const res = await fetch(`${remoteUrl.replace(/\/$/, "")}/tunnels`, {
|
|
16978
|
+
method: "POST",
|
|
16979
|
+
headers: {
|
|
16980
|
+
"Content-Type": "application/json",
|
|
16981
|
+
Authorization: `Bearer ${authKey3}`,
|
|
16982
|
+
...setupSecret ? { "X-Tunnel-Setup-Secret": setupSecret } : {}
|
|
16983
|
+
},
|
|
16984
|
+
body: JSON.stringify(reqBody)
|
|
16985
|
+
});
|
|
16986
|
+
if (!res.ok) {
|
|
16987
|
+
const body = await res.text();
|
|
16988
|
+
console.log(chalk.red(`Server refused tunnel creation (HTTP ${res.status}): ${body}`));
|
|
16989
|
+
process.exitCode = 1;
|
|
16990
|
+
return;
|
|
16991
|
+
}
|
|
16992
|
+
const { tunnelToken, hostname: provisionedHost, apiHostname } = await res.json();
|
|
16993
|
+
const cfPath = await installCloudflaredIfNeeded();
|
|
16994
|
+
if (!cfPath) {
|
|
16995
|
+
console.log(chalk.yellow("cloudflared not installed; run this once installed:"));
|
|
16996
|
+
console.log(chalk.cyan(` cloudflared service install ${tunnelToken}`));
|
|
16997
|
+
return;
|
|
16998
|
+
}
|
|
16999
|
+
console.log(chalk.bold("\nInstalling cloudflared as a service..."));
|
|
17000
|
+
const ok = installAsService(tunnelToken);
|
|
17001
|
+
if (!ok) {
|
|
17002
|
+
console.log(chalk.yellow("Service install failed; you can run it manually:"));
|
|
17003
|
+
console.log(chalk.cyan(` cloudflared tunnel run --token ${tunnelToken}`));
|
|
17004
|
+
}
|
|
17005
|
+
console.log("");
|
|
17006
|
+
console.log(chalk.green("Done."), "Your sparkecoder will be reachable at:");
|
|
17007
|
+
console.log(" Web:", chalk.cyan(`https://${provisionedHost}`));
|
|
17008
|
+
console.log(" API:", chalk.cyan(`https://${apiHostname}`));
|
|
17009
|
+
return;
|
|
17010
|
+
} catch (err) {
|
|
17011
|
+
console.error(chalk.red("Remote tunnel setup failed:"), err?.message || err);
|
|
17012
|
+
process.exitCode = 1;
|
|
17013
|
+
return;
|
|
17014
|
+
}
|
|
17015
|
+
}
|
|
17016
|
+
const cfToken = options.cfApiToken || process.env.CF_API_TOKEN;
|
|
17017
|
+
const cfAccount = options.cfAccountId || process.env.CF_ACCOUNT_ID;
|
|
17018
|
+
const cfZone = options.cfZoneId || process.env.CF_ZONE_ID;
|
|
17019
|
+
if (cfToken && cfAccount) {
|
|
17020
|
+
try {
|
|
17021
|
+
const hostname = options.hostname;
|
|
17022
|
+
if (!hostname) {
|
|
17023
|
+
console.log(chalk.red("--hostname is required in API mode."));
|
|
17024
|
+
process.exitCode = 1;
|
|
17025
|
+
return;
|
|
17026
|
+
}
|
|
17027
|
+
const cfPath = await installCloudflaredIfNeeded();
|
|
17028
|
+
if (!cfPath) {
|
|
17029
|
+
console.log(chalk.red("cloudflared is required. Install from https://github.com/cloudflare/cloudflared/releases and re-run."));
|
|
17030
|
+
process.exitCode = 1;
|
|
17031
|
+
return;
|
|
17032
|
+
}
|
|
17033
|
+
const { createRemoteTunnel: createRemoteTunnel2, setTunnelConfig: setTunnelConfig2, upsertDnsCname: upsertDnsCname2 } = await Promise.resolve().then(() => (init_api(), api_exports));
|
|
17034
|
+
const cfg = { apiToken: cfToken, accountId: cfAccount, zoneId: cfZone };
|
|
17035
|
+
console.log(chalk.bold("Creating tunnel via Cloudflare API..."));
|
|
17036
|
+
const created = await createRemoteTunnel2(cfg, tunnelName || `sparkecoder-${hostname}`);
|
|
17037
|
+
console.log(chalk.green("\u2713"), `tunnel created: ${created.id}`);
|
|
17038
|
+
await setTunnelConfig2(cfg, created.id, [
|
|
17039
|
+
{ hostname, service: `http://localhost:${port}` },
|
|
17040
|
+
{ hostname: `api-${hostname}`, service: `http://localhost:${apiPort}` }
|
|
17041
|
+
]);
|
|
17042
|
+
console.log(chalk.green("\u2713"), "ingress config set");
|
|
17043
|
+
if (cfZone) {
|
|
17044
|
+
await upsertDnsCname2(cfg, hostname, created.id);
|
|
17045
|
+
await upsertDnsCname2(cfg, `api-${hostname}`, created.id);
|
|
17046
|
+
console.log(chalk.green("\u2713"), `DNS routes: ${hostname}, api-${hostname}`);
|
|
17047
|
+
} else {
|
|
17048
|
+
console.log(chalk.yellow("Skipping DNS (no --cf-zone-id). Add CNAMEs manually:"));
|
|
17049
|
+
console.log(chalk.dim(` ${hostname} CNAME -> ${created.id}.cfargotunnel.com (proxied)`));
|
|
17050
|
+
console.log(chalk.dim(` api-${hostname} CNAME -> ${created.id}.cfargotunnel.com (proxied)`));
|
|
17051
|
+
}
|
|
17052
|
+
console.log(chalk.bold("\nInstalling cloudflared as a service..."));
|
|
17053
|
+
installAsService(created.token);
|
|
17054
|
+
console.log("");
|
|
17055
|
+
console.log(chalk.green("Done."), "Your sparkecoder will be reachable at:");
|
|
17056
|
+
console.log(" Web:", chalk.cyan(`https://${hostname}`));
|
|
17057
|
+
console.log(" API:", chalk.cyan(`https://api-${hostname}`));
|
|
17058
|
+
return;
|
|
17059
|
+
} catch (err) {
|
|
17060
|
+
console.error(chalk.red("API-mode setup failed:"), err?.message || err);
|
|
17061
|
+
process.exitCode = 1;
|
|
17062
|
+
return;
|
|
17063
|
+
}
|
|
17064
|
+
}
|
|
16841
17065
|
if (options.printOnly) {
|
|
16842
17066
|
const hostname = options.hostname || "<your-public-hostname>";
|
|
16843
17067
|
console.log(chalk.bold("Cloudflared + Cloudflare Access setup (manual)\n"));
|