@roxyon/deploy-core 0.1.1 → 0.3.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/dist/index.cjs +98 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +98 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -35,17 +35,20 @@ __export(index_exports, {
|
|
|
35
35
|
PROJECT_FILE: () => PROJECT_FILE,
|
|
36
36
|
buildIgnore: () => buildIgnore,
|
|
37
37
|
buildProjectConfig: () => buildProjectConfig,
|
|
38
|
+
buildWpConfig: () => buildWpConfig,
|
|
38
39
|
clearCredentials: () => clearCredentials,
|
|
39
40
|
credentialsDir: () => credentialsDir,
|
|
40
41
|
credentialsPath: () => credentialsPath,
|
|
41
42
|
deployProject: () => deployProject,
|
|
42
43
|
detectRuntime: () => detectRuntime,
|
|
43
44
|
endpointsFromEnv: () => endpointsFromEnv,
|
|
45
|
+
installWordPress: () => installWordPress,
|
|
44
46
|
listFiles: () => listFiles,
|
|
45
47
|
loadCredentials: () => loadCredentials,
|
|
46
48
|
loadProjectConfig: () => loadProjectConfig,
|
|
47
49
|
measure: () => measure,
|
|
48
50
|
packDirectory: () => packDirectory,
|
|
51
|
+
packFiles: () => packFiles,
|
|
49
52
|
projectConfigPath: () => projectConfigPath,
|
|
50
53
|
runShellCommand: () => runShellCommand,
|
|
51
54
|
saveCredentials: () => saveCredentials,
|
|
@@ -56,6 +59,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
56
59
|
|
|
57
60
|
// src/archive.ts
|
|
58
61
|
var import_promises = require("fs/promises");
|
|
62
|
+
var import_node_os = require("os");
|
|
59
63
|
var import_node_path = require("path");
|
|
60
64
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
61
65
|
var import_tar = require("tar");
|
|
@@ -136,6 +140,44 @@ async function packDirectory(root, opts = {}) {
|
|
|
136
140
|
}
|
|
137
141
|
return { buffer: Buffer.concat(chunks), files, bytes };
|
|
138
142
|
}
|
|
143
|
+
function safeEntryPath(p) {
|
|
144
|
+
const norm = p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
145
|
+
if (!norm || norm.startsWith("/") || (0, import_node_path.isAbsolute)(norm) || norm.includes("\0")) return null;
|
|
146
|
+
if (norm.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) return null;
|
|
147
|
+
return norm;
|
|
148
|
+
}
|
|
149
|
+
async function packFiles(entries, opts = {}) {
|
|
150
|
+
if (entries.length === 0) throw new Error("No files to deploy.");
|
|
151
|
+
const seen = /* @__PURE__ */ new Set();
|
|
152
|
+
const clean = [];
|
|
153
|
+
let total = 0;
|
|
154
|
+
for (const e of entries) {
|
|
155
|
+
const rel = safeEntryPath(e.path);
|
|
156
|
+
if (!rel) throw new Error(`Unsafe file path: ${JSON.stringify(e.path)}`);
|
|
157
|
+
if (seen.has(rel)) throw new Error(`Duplicate file path: ${rel}`);
|
|
158
|
+
seen.add(rel);
|
|
159
|
+
const buf = Buffer.from(e.content, e.encoding === "base64" ? "base64" : "utf8");
|
|
160
|
+
total += buf.length;
|
|
161
|
+
clean.push({ path: rel, buf });
|
|
162
|
+
}
|
|
163
|
+
const max = opts.maxBytes ?? 512 * 1024 * 1024;
|
|
164
|
+
if (total > max) {
|
|
165
|
+
throw new Error(
|
|
166
|
+
`Content is ${(total / 1024 / 1024).toFixed(1)} MB, which exceeds the ${(max / 1024 / 1024).toFixed(1)} MB limit.`
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const dir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "roxyon-content-"));
|
|
170
|
+
try {
|
|
171
|
+
for (const { path: rel, buf } of clean) {
|
|
172
|
+
const abs = (0, import_node_path.join)(dir, rel);
|
|
173
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(abs), { recursive: true });
|
|
174
|
+
await (0, import_promises.writeFile)(abs, buf);
|
|
175
|
+
}
|
|
176
|
+
return await packDirectory(dir, opts);
|
|
177
|
+
} finally {
|
|
178
|
+
await (0, import_promises.rm)(dir, { recursive: true, force: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
139
181
|
async function measure(root, files) {
|
|
140
182
|
let total = 0;
|
|
141
183
|
for (const f of files) {
|
|
@@ -457,12 +499,57 @@ async function watch(roxyon, appId, opts) {
|
|
|
457
499
|
};
|
|
458
500
|
}
|
|
459
501
|
|
|
460
|
-
// src/
|
|
502
|
+
// src/wordpress.ts
|
|
461
503
|
var import_promises5 = require("fs/promises");
|
|
462
|
-
var
|
|
504
|
+
var import_node_stream = require("stream");
|
|
505
|
+
var import_tar2 = require("tar");
|
|
506
|
+
var WORDPRESS_TARBALL_URL = "https://wordpress.org/latest.tar.gz";
|
|
507
|
+
var WORDPRESS_SALTS_URL = "https://api.wordpress.org/secret-key/1.1/salt/";
|
|
508
|
+
var PLACEHOLDER = "put your unique phrase here";
|
|
509
|
+
async function installWordPress(opts) {
|
|
510
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
511
|
+
await (0, import_promises5.mkdir)(opts.dir, { recursive: true });
|
|
512
|
+
const coreRes = await fetchImpl(WORDPRESS_TARBALL_URL);
|
|
513
|
+
if (!coreRes.ok) {
|
|
514
|
+
throw new Error(`Could not download WordPress core (HTTP ${coreRes.status}).`);
|
|
515
|
+
}
|
|
516
|
+
const coreBuf = Buffer.from(await coreRes.arrayBuffer());
|
|
517
|
+
await new Promise((resolvePromise, reject) => {
|
|
518
|
+
const writer = (0, import_tar2.extract)({ cwd: opts.dir, strip: 1 });
|
|
519
|
+
import_node_stream.Readable.from(coreBuf).pipe(writer).on("finish", () => resolvePromise()).on("error", reject);
|
|
520
|
+
});
|
|
521
|
+
const saltsRes = await fetchImpl(WORDPRESS_SALTS_URL);
|
|
522
|
+
const salts = saltsRes.ok ? (await saltsRes.text()).trim() : "";
|
|
523
|
+
const samplePath = `${opts.dir}/wp-config-sample.php`;
|
|
524
|
+
const sample = await (0, import_promises5.readFile)(samplePath, "utf8");
|
|
525
|
+
const config = buildWpConfig(sample, opts.db, opts.tablePrefix, salts);
|
|
526
|
+
await (0, import_promises5.writeFile)(`${opts.dir}/wp-config.php`, config, "utf8");
|
|
527
|
+
return { dir: opts.dir };
|
|
528
|
+
}
|
|
529
|
+
function buildWpConfig(sample, db, tablePrefix = "wp_", salts = "") {
|
|
530
|
+
let out = sample.replace(/(['"])database_name_here\1/, `$1${escapePhp(db.name)}$1`).replace(/(['"])username_here\1/, `$1${escapePhp(db.username)}$1`).replace(/(['"])password_here\1/, `$1${escapePhp(db.password)}$1`).replace(/(define\(\s*['"]DB_HOST['"]\s*,\s*)(['"])localhost\2/, `$1$2${escapePhp(db.host)}$2`).replace(/(\$table_prefix\s*=\s*)(['"])wp_\2/, `$1$2${escapePhp(tablePrefix)}$2`);
|
|
531
|
+
if (salts) {
|
|
532
|
+
const lines = out.split("\n");
|
|
533
|
+
const start = lines.findIndex((l) => l.includes(PLACEHOLDER));
|
|
534
|
+
if (start !== -1) {
|
|
535
|
+
let end = start;
|
|
536
|
+
while (end < lines.length && lines[end].includes(PLACEHOLDER)) end++;
|
|
537
|
+
lines.splice(start, end - start, salts);
|
|
538
|
+
out = lines.join("\n");
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return out;
|
|
542
|
+
}
|
|
543
|
+
function escapePhp(s) {
|
|
544
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// src/credentials.ts
|
|
548
|
+
var import_promises6 = require("fs/promises");
|
|
549
|
+
var import_node_os2 = require("os");
|
|
463
550
|
var import_node_path5 = require("path");
|
|
464
551
|
function credentialsDir() {
|
|
465
|
-
return (0, import_node_path5.join)((0,
|
|
552
|
+
return (0, import_node_path5.join)((0, import_node_os2.homedir)(), ".roxyon");
|
|
466
553
|
}
|
|
467
554
|
function credentialsPath() {
|
|
468
555
|
return (0, import_node_path5.join)(credentialsDir(), "config.json");
|
|
@@ -478,21 +565,21 @@ function tokenFromEnv() {
|
|
|
478
565
|
}
|
|
479
566
|
async function loadCredentials() {
|
|
480
567
|
try {
|
|
481
|
-
return JSON.parse(await (0,
|
|
568
|
+
return JSON.parse(await (0, import_promises6.readFile)(credentialsPath(), "utf8"));
|
|
482
569
|
} catch {
|
|
483
570
|
return void 0;
|
|
484
571
|
}
|
|
485
572
|
}
|
|
486
573
|
async function saveCredentials(creds) {
|
|
487
574
|
const path = credentialsPath();
|
|
488
|
-
await (0,
|
|
489
|
-
await (0,
|
|
575
|
+
await (0, import_promises6.mkdir)(credentialsDir(), { recursive: true, mode: 448 });
|
|
576
|
+
await (0, import_promises6.writeFile)(path, `${JSON.stringify(creds, null, 2)}
|
|
490
577
|
`, { mode: 384 });
|
|
491
|
-
await (0,
|
|
578
|
+
await (0, import_promises6.chmod)(path, 384).catch(() => void 0);
|
|
492
579
|
return path;
|
|
493
580
|
}
|
|
494
581
|
async function clearCredentials() {
|
|
495
|
-
await (0,
|
|
582
|
+
await (0, import_promises6.rm)(credentialsPath(), { force: true });
|
|
496
583
|
}
|
|
497
584
|
// Annotate the CommonJS export names for ESM import in node:
|
|
498
585
|
0 && (module.exports = {
|
|
@@ -501,17 +588,20 @@ async function clearCredentials() {
|
|
|
501
588
|
PROJECT_FILE,
|
|
502
589
|
buildIgnore,
|
|
503
590
|
buildProjectConfig,
|
|
591
|
+
buildWpConfig,
|
|
504
592
|
clearCredentials,
|
|
505
593
|
credentialsDir,
|
|
506
594
|
credentialsPath,
|
|
507
595
|
deployProject,
|
|
508
596
|
detectRuntime,
|
|
509
597
|
endpointsFromEnv,
|
|
598
|
+
installWordPress,
|
|
510
599
|
listFiles,
|
|
511
600
|
loadCredentials,
|
|
512
601
|
loadProjectConfig,
|
|
513
602
|
measure,
|
|
514
603
|
packDirectory,
|
|
604
|
+
packFiles,
|
|
515
605
|
projectConfigPath,
|
|
516
606
|
runShellCommand,
|
|
517
607
|
saveCredentials,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/archive.ts","../src/detect.ts","../src/project.ts","../src/deploy.ts","../src/run-command.ts","../src/credentials.ts"],"sourcesContent":["export {\n ALWAYS_IGNORE,\n buildIgnore,\n listFiles,\n measure,\n packDirectory,\n type PackResult,\n} from './archive.js';\nexport { type Detection, detectRuntime } from './detect.js';\nexport {\n buildProjectConfig,\n type BuildConfigInput,\n type DeployKind,\n loadProjectConfig,\n PROJECT_FILE,\n type ProjectConfig,\n projectConfigPath,\n saveProjectConfig,\n} from './project.js';\nexport {\n deployProject,\n DeployError,\n type DeployOutcome,\n type DeployParams,\n type DeployReporter,\n} from './deploy.js';\nexport { runShellCommand } from './run-command.js';\nexport {\n type Credentials,\n credentialsDir,\n credentialsPath,\n clearCredentials,\n type Endpoints,\n endpointsFromEnv,\n loadCredentials,\n saveCredentials,\n tokenFromEnv,\n} from './credentials.js';\n","import { readFile, readdir, stat } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport ignore, { type Ignore } from 'ignore';\nimport { create as tarCreate } from 'tar';\n\n/** Always excluded, regardless of ignore files — build artefacts and VCS/OS cruft. */\nexport const ALWAYS_IGNORE = [\n 'node_modules',\n '.git',\n '.next',\n '.nuxt',\n '.venv',\n '__pycache__',\n '.DS_Store',\n '.roxyon',\n 'dist/.cache',\n];\n\nasync function readIgnoreFile(root: string, name: string): Promise<string[]> {\n try {\n return (await readFile(join(root, name), 'utf8')).split(/\\r?\\n/);\n } catch {\n return [];\n }\n}\n\n/** Build the ignore matcher: `.roxyonignore` if present, else `.gitignore`, plus the always-list. */\nexport async function buildIgnore(root: string): Promise<Ignore> {\n const ig = ignore().add(ALWAYS_IGNORE);\n const roxyon = await readIgnoreFile(root, '.roxyonignore');\n if (roxyon.length) ig.add(roxyon);\n else ig.add(await readIgnoreFile(root, '.gitignore'));\n return ig;\n}\n\n/** Recursively list files under `root` that survive the ignore matcher (POSIX-relative paths). */\nexport async function listFiles(root: string, ig: Ignore): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const abs = join(dir, entry.name);\n const rel = relative(root, abs).split(sep).join('/');\n if (!rel || ig.ignores(rel) || ig.ignores(`${rel}/`)) continue;\n if (entry.isDirectory()) {\n await walk(abs);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(rel);\n }\n }\n }\n\n await walk(root);\n return files.sort();\n}\n\nexport interface PackResult {\n buffer: Buffer;\n files: string[];\n bytes: number;\n}\n\n/**\n * Create a deterministic gzipped tarball of `root` (minus ignored paths).\n * `prefix`, when given, is prepended to every entry path inside the archive.\n */\nexport async function packDirectory(\n root: string,\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n const ig = await buildIgnore(root);\n const files = await listFiles(root, ig);\n if (files.length === 0) {\n throw new Error(\n `Nothing to deploy from ${root} — every file is ignored or the folder is empty.`,\n );\n }\n\n const stream = tarCreate(\n {\n gzip: true,\n cwd: root,\n portable: true,\n mtime: new Date(0), // stable -> byte-identical archive for an unchanged tree\n prefix: opts.prefix,\n follow: false,\n },\n files,\n );\n\n const chunks: Buffer[] = [];\n let bytes = 0;\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n for await (const chunk of stream as AsyncIterable<Buffer>) {\n bytes += chunk.length;\n if (bytes > max) {\n throw new Error(\n `Deploy archive exceeds ${(max / 1024 / 1024) | 0} MB. Add large files to .roxyonignore.`,\n );\n }\n chunks.push(chunk);\n }\n\n return { buffer: Buffer.concat(chunks), files, bytes };\n}\n\n/** Total on-disk size of a set of files. */\nexport async function measure(root: string, files: string[]): Promise<number> {\n let total = 0;\n for (const f of files) {\n try {\n total += (await stat(join(root, f))).size;\n } catch {\n /* ignore */\n }\n }\n return total;\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { RuntimeName } from '@roxyon/api-client';\n\nexport interface Detection {\n runtime: RuntimeName;\n /** why we think so — shown to the user, who can override */\n reason: string;\n /** best-guess start command for a server runtime */\n start?: string;\n}\n\nasync function exists(root: string, name: string): Promise<boolean> {\n try {\n await readFile(join(root, name));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJson(root: string, name: string): Promise<Record<string, unknown> | undefined> {\n try {\n return JSON.parse(await readFile(join(root, name), 'utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort project-type detection. Deterministic; the caller confirms. */\nexport async function detectRuntime(root: string): Promise<Detection> {\n const pkg = await readJson(root, 'package.json');\n const hasConfigJson = await exists(root, 'config.json');\n let hasViews = false;\n try {\n hasViews = (await readdir(join(root, 'src', 'views'))).length >= 0;\n } catch {\n hasViews = false;\n }\n\n if (hasConfigJson && hasViews) {\n const deps = {\n ...(pkg?.dependencies as object),\n ...(pkg?.devDependencies as object),\n } as Record<string, string>;\n if (!('next' in deps) && !('nuxt' in deps) && !('express' in deps) && !('fastify' in deps)) {\n return { runtime: 'lumen', reason: 'config.json + src/views/ and no server framework' };\n }\n }\n\n if (pkg) {\n const scripts = (pkg.scripts as Record<string, string>) ?? {};\n const start = scripts.start ? 'npm run start' : scripts.serve ? 'npm run serve' : 'node .';\n const deps = { ...(pkg.dependencies as object), ...(pkg.devDependencies as object) } as Record<\n string,\n string\n >;\n if ('next' in deps)\n return { runtime: 'node', reason: 'Next.js in package.json', start: 'npm run start' };\n if ('nuxt' in deps)\n return { runtime: 'node', reason: 'Nuxt in package.json', start: 'npm run start' };\n return { runtime: 'node', reason: 'package.json present', start };\n }\n\n if ((await exists(root, 'requirements.txt')) || (await exists(root, 'pyproject.toml'))) {\n return {\n runtime: 'python',\n reason: 'requirements.txt / pyproject.toml',\n start: 'gunicorn app:app',\n };\n }\n\n if ((await exists(root, 'composer.json')) || (await exists(root, 'server.php'))) {\n return { runtime: 'php', reason: 'composer.json / server.php', start: 'php8.4 server.php' };\n }\n\n return { runtime: 'lumen', reason: 'no framework markers — treating as a static site' };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname, resolve } from 'node:path';\nimport { type DeployKind, LUMEN_BUILD, RUNTIMES, type RuntimeName } from '@roxyon/api-client';\n\nexport type { DeployKind };\n\nexport interface ProjectConfig {\n name: string;\n /** `Applications.objectId`, filled after the first `app`-kind deploy. */\n application?: string;\n /** Route host — `Domains.Name`. */\n host: string;\n /** Sub-path under the host's docroot / source tree. `\"\"` = root. */\n folder: string;\n runtime: RuntimeName;\n runtimeVersion?: string;\n preset?: string;\n kind: DeployKind;\n /** Local build command (LumenJS / static). Empty for server runtimes. */\n build?: string;\n /** Directory the build writes to (relative to project root). */\n outDir?: string;\n /** Web-process start command (app kind only). */\n start?: string;\n /** Serve the app on the host instead of the host's own files. */\n public?: boolean;\n}\n\nexport const PROJECT_FILE = 'roxyon.json';\n\nexport function projectConfigPath(cwd: string = process.cwd()): string {\n return resolve(cwd, PROJECT_FILE);\n}\n\nexport async function loadProjectConfig(\n cwd: string = process.cwd(),\n): Promise<ProjectConfig | undefined> {\n try {\n return JSON.parse(await readFile(projectConfigPath(cwd), 'utf8')) as ProjectConfig;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveProjectConfig(\n config: ProjectConfig,\n cwd: string = process.cwd(),\n): Promise<string> {\n const path = projectConfigPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`);\n return path;\n}\n\nexport interface BuildConfigInput {\n cwd: string;\n runtime: RuntimeName;\n host: string;\n folder?: string;\n name?: string;\n start?: string;\n}\n\n/**\n * Produce a `ProjectConfig` from a resolved runtime + host. Shared by the CLI's\n * `roxyon init` and the MCP `roxyon_init` tool so both write the same shape.\n */\nexport function buildProjectConfig(input: BuildConfigInput): ProjectConfig {\n const folder = String(input.folder ?? '').replace(/^\\/+|\\/+$/g, '');\n const config: ProjectConfig = {\n name: input.name ?? basename(input.cwd),\n host: input.host,\n folder,\n runtime: input.runtime,\n kind: input.runtime === 'lumen' ? 'static' : 'app',\n };\n\n if (input.runtime === 'lumen') {\n config.build = LUMEN_BUILD.command;\n config.outDir = LUMEN_BUILD.outDir;\n } else {\n const spec = RUNTIMES[input.runtime];\n config.runtimeVersion = spec.defaultVersion;\n config.preset = spec.presets[0]?.[0];\n config.start = input.start ?? spec.command;\n config.public = true;\n config.build = '';\n }\n return config;\n}\n","import { access } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n type AccountApp,\n RUNTIMES,\n type Roxyon,\n RoxyonApiError,\n isAuthError,\n} from '@roxyon/api-client';\nimport { packDirectory } from './archive.js';\nimport { type ProjectConfig, loadProjectConfig, saveProjectConfig } from './project.js';\nimport { runShellCommand } from './run-command.js';\n\nexport interface DeployReporter {\n /** A high-level phase change (\"Building\", \"Uploading\", \"Live\"). */\n step?: (msg: string) => void;\n /** A single build- or runtime-log line. */\n log?: (line: string) => void;\n}\n\nexport interface DeployParams {\n cwd: string;\n /** An authenticated client. */\n roxyon: Roxyon;\n /** Subscription objectId / Name to prefer when the account has several. */\n preferredSubscription?: string;\n /** Run the configured build command (default true). */\n build?: boolean;\n /** For app deploys: poll until running/failed (default true). */\n follow?: boolean;\n pollMs?: number;\n timeoutMs?: number;\n reporter?: DeployReporter;\n /** Override the build-command runner (default: spawn a shell). */\n runCommand?: (command: string, cwd: string, reporter?: DeployReporter) => Promise<number>;\n}\n\nexport type DeployOutcome =\n | { kind: 'static'; ok: true; url: string; files: number; config: ProjectConfig }\n | {\n kind: 'app';\n ok: true;\n application: string;\n status: string;\n revision: number;\n url?: string;\n config: ProjectConfig;\n logs: string[];\n }\n | {\n kind: 'app';\n ok: false;\n application: string;\n status: string;\n error: string;\n config: ProjectConfig;\n logs: string[];\n };\n\nexport class DeployError extends Error {\n readonly code:\n | 'no-config'\n | 'auth'\n | 'bad-host'\n | 'build-failed'\n | 'no-build-output'\n | 'upload-failed'\n | 'create-failed';\n constructor(message: string, code: DeployError['code']) {\n super(message);\n this.name = 'DeployError';\n this.code = code;\n }\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Build (if configured), archive, upload, and — for app runtimes — poll until\n * the deploy settles. Self-contained: one `/account/context` call validates the\n * host (works with a session token or a `roxp_` PAT), and the console creates\n * the application on the first deploy — the new id is written back to\n * `roxyon.json`.\n */\nexport async function deployProject(params: DeployParams): Promise<DeployOutcome> {\n const { cwd, roxyon, reporter } = params;\n const runCommand = params.runCommand ?? runShellCommand;\n\n const config = await loadProjectConfig(cwd);\n if (!config) {\n throw new DeployError('No roxyon.json in this directory — run init first.', 'no-config');\n }\n\n // One call — works with a session token or a PAT.\n const ctx = await roxyon.account.context().catch((e) => {\n if (isAuthError(e)) throw new DeployError('Session expired — sign in again.', 'auth');\n throw e;\n });\n const domain = ctx.domains.find((d) => d.name === config.host);\n if (!domain) {\n throw new DeployError(\n `Host \"${config.host}\" is not on this account (have: ${\n ctx.domains.map((d) => d.name).join(', ') || 'none'\n }).`,\n 'bad-host',\n );\n }\n\n // ---- build ----\n if (config.build && params.build !== false) {\n reporter?.step?.(`Build: ${config.build}`);\n const code = await runCommand(config.build, cwd, reporter);\n if (code !== 0) throw new DeployError(`Build command exited ${code}.`, 'build-failed');\n }\n\n // ---- pack ----\n const artifactDir = config.kind === 'static' && config.outDir ? resolve(cwd, config.outDir) : cwd;\n if (config.kind === 'static' && config.outDir) {\n await access(artifactDir).catch(() => {\n throw new DeployError(\n `Build output \"${config.outDir}\" not found — did the build run?`,\n 'no-build-output',\n );\n });\n }\n reporter?.step?.(`Packing ${artifactDir === cwd ? '.' : config.outDir}`);\n const pack = await packDirectory(artifactDir);\n reporter?.log?.(`${pack.files.length} files · ${(pack.bytes / 1024).toFixed(0)} KB`);\n\n // ---- static ----\n if (config.kind === 'static') {\n reporter?.step?.(`Uploading to ${config.host}${config.folder ? `/${config.folder}` : ''}`);\n let res: Awaited<ReturnType<typeof roxyon.sites.deploy>>;\n try {\n res = await roxyon.sites.deploy(config.host, config.folder, pack.buffer);\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n 'upload-failed',\n );\n }\n const url = `https://${res.host}${res.path && res.path !== '/' ? res.path : ''}`;\n reporter?.step?.(`Live — ${url}`);\n return { kind: 'static', ok: true, url, files: res.files ?? pack.files.length, config };\n }\n\n // ---- app ----\n // The console's /applications/deploy creates the app rows on the first deploy\n // (no `application` id yet) from the host + runtime params — one trusted call,\n // so a PAT-only CI job needs no BaaS write access.\n const runtime = config.runtime === 'lumen' ? 'node' : config.runtime;\n const spec = RUNTIMES[runtime];\n const target = config.application ?? {\n host: config.host,\n folder: config.folder,\n runtime,\n runtimeVersion: config.runtimeVersion ?? spec.defaultVersion,\n preset: config.preset ?? spec.presets[0]?.[0] ?? runtime,\n command: config.start ?? spec.command,\n public: config.public ?? true,\n };\n\n reporter?.step?.(\n config.application ? 'Uploading source' : 'Creating the application & uploading',\n );\n let revision = 0;\n let appId: string;\n try {\n const res = await roxyon.applications.uploadSource(target, pack.buffer);\n revision = res.configRevision ?? 0;\n appId = res.application;\n if (res.created && appId && appId !== config.application) {\n config.application = appId;\n await saveProjectConfig(config, cwd);\n reporter?.log?.(`application ${appId} created`);\n }\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n config.application ? 'upload-failed' : 'create-failed',\n );\n }\n\n const url = config.public\n ? `https://${config.host}${config.folder ? `/${config.folder}` : ''}`\n : undefined;\n\n if (params.follow === false) {\n reporter?.step?.(`Build queued (revision ${revision})`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'building',\n revision,\n url,\n config,\n logs: [],\n };\n }\n\n return watch(roxyon, appId, {\n reporter,\n url,\n config,\n pollMs: params.pollMs ?? 3000,\n timeoutMs: params.timeoutMs ?? 10 * 60 * 1000,\n });\n}\n\nasync function watch(\n roxyon: Roxyon,\n appId: string,\n opts: {\n reporter?: DeployReporter;\n url?: string;\n config: ProjectConfig;\n pollMs: number;\n timeoutMs: number;\n },\n): Promise<DeployOutcome> {\n const started = Date.now();\n const seen = new Set<string>();\n const collected: string[] = [];\n let lastStatus = '';\n\n opts.reporter?.step?.('Building');\n while (true) {\n await sleep(opts.pollMs);\n\n let app: AccountApp | undefined;\n try {\n app = await roxyon.account.getApp(appId);\n } catch {\n /* transient */\n }\n if (!app) {\n if (Date.now() - started > opts.timeoutMs) break;\n continue;\n }\n\n const cfg = app.configRevision;\n const applied = app.appliedRevision;\n const status = app.status;\n if (status !== lastStatus) {\n opts.reporter?.step?.(`${status || 'pending'} (${applied}/${cfg})`);\n lastStatus = status;\n }\n\n try {\n for (const line of await roxyon.applications.logs(appId, 40)) {\n if (line && !seen.has(line)) {\n seen.add(line);\n collected.push(line);\n opts.reporter?.log?.(line);\n }\n }\n } catch {\n /* transient */\n }\n\n if (cfg <= applied && status === 'running') {\n opts.reporter?.step?.(`Live${opts.url ? ` — ${opts.url}` : ''}`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'running',\n revision: applied,\n url: opts.url,\n config: opts.config,\n logs: collected,\n };\n }\n if (status === 'failed') {\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'failed',\n error: (app.lastError || 'Deploy failed.').trim(),\n config: opts.config,\n logs: collected,\n };\n }\n if (Date.now() - started > opts.timeoutMs) break;\n }\n\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'timeout',\n error: 'Still building after the timeout — check `roxyon logs`.',\n config: opts.config,\n logs: collected,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { DeployReporter } from './deploy.js';\n\n/**\n * Default build-command runner: spawn a shell, stream output to the reporter.\n * Callers in a non-local context (a hosted server) can pass their own.\n */\nexport function runShellCommand(\n command: string,\n cwd: string,\n reporter?: DeployReporter,\n): Promise<number> {\n return new Promise((resolvePromise) => {\n const child = spawn(command, { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n const pipe = (buf: Buffer) => {\n for (const line of buf.toString('utf8').split('\\n')) {\n if (line.trim()) reporter?.log?.(line);\n }\n };\n child.stdout?.on('data', pipe);\n child.stderr?.on('data', pipe);\n child.on('close', (code) => resolvePromise(code ?? 0));\n child.on('error', () => resolvePromise(127));\n });\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written\n * by `roxyon login`; read by the CLI and the MCP server alike.\n */\nexport interface Credentials {\n sessionToken: string;\n refreshToken?: string;\n userId: string;\n email?: string;\n /** Preferred subscription (objectId / Name) when the account has several. */\n subscription?: string;\n savedAt: string;\n}\n\nexport interface Endpoints {\n apiUrl?: string;\n consoleUrl?: string;\n}\n\nexport function credentialsDir(): string {\n return join(homedir(), '.roxyon');\n}\nexport function credentialsPath(): string {\n return join(credentialsDir(), 'config.json');\n}\n\n/** Endpoint overrides from the environment (`ROXYON_API_URL`, `ROXYON_CONSOLE_URL`). */\nexport function endpointsFromEnv(): Endpoints {\n return {\n apiUrl: process.env.ROXYON_API_URL,\n consoleUrl: process.env.ROXYON_CONSOLE_URL,\n };\n}\n\n/** A token supplied out-of-band (CI, or an MCP host env). Skips the stored login. */\nexport function tokenFromEnv(): string | undefined {\n return process.env.ROXYON_TOKEN || undefined;\n}\n\nexport async function loadCredentials(): Promise<Credentials | undefined> {\n try {\n return JSON.parse(await readFile(credentialsPath(), 'utf8')) as Credentials;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveCredentials(creds: Credentials): Promise<string> {\n const path = credentialsPath();\n await mkdir(credentialsDir(), { recursive: true, mode: 0o700 });\n await writeFile(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600).catch(() => undefined);\n return path;\n}\n\nexport async function clearCredentials(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n"],"mappings":"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","names":["ignore","tarCreate","import_promises","import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_api_client","url","import_promises","import_node_path"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/archive.ts","../src/detect.ts","../src/project.ts","../src/deploy.ts","../src/run-command.ts","../src/wordpress.ts","../src/credentials.ts"],"sourcesContent":["export {\n ALWAYS_IGNORE,\n buildIgnore,\n type FileEntry,\n listFiles,\n measure,\n packDirectory,\n packFiles,\n type PackResult,\n} from './archive.js';\nexport { type Detection, detectRuntime } from './detect.js';\nexport {\n buildProjectConfig,\n type BuildConfigInput,\n type DeployKind,\n loadProjectConfig,\n PROJECT_FILE,\n type ProjectConfig,\n projectConfigPath,\n saveProjectConfig,\n} from './project.js';\nexport {\n deployProject,\n DeployError,\n type DeployOutcome,\n type DeployParams,\n type DeployReporter,\n} from './deploy.js';\nexport { runShellCommand } from './run-command.js';\nexport {\n buildWpConfig,\n installWordPress,\n type InstallWordPressOptions,\n type InstallWordPressResult,\n type WordPressDbCredentials,\n} from './wordpress.js';\nexport {\n type Credentials,\n credentialsDir,\n credentialsPath,\n clearCredentials,\n type Endpoints,\n endpointsFromEnv,\n loadCredentials,\n saveCredentials,\n tokenFromEnv,\n} from './credentials.js';\n","import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, relative, sep } from 'node:path';\nimport ignore, { type Ignore } from 'ignore';\nimport { create as tarCreate } from 'tar';\n\n/** Always excluded, regardless of ignore files — build artefacts and VCS/OS cruft. */\nexport const ALWAYS_IGNORE = [\n 'node_modules',\n '.git',\n '.next',\n '.nuxt',\n '.venv',\n '__pycache__',\n '.DS_Store',\n '.roxyon',\n 'dist/.cache',\n];\n\nasync function readIgnoreFile(root: string, name: string): Promise<string[]> {\n try {\n return (await readFile(join(root, name), 'utf8')).split(/\\r?\\n/);\n } catch {\n return [];\n }\n}\n\n/** Build the ignore matcher: `.roxyonignore` if present, else `.gitignore`, plus the always-list. */\nexport async function buildIgnore(root: string): Promise<Ignore> {\n const ig = ignore().add(ALWAYS_IGNORE);\n const roxyon = await readIgnoreFile(root, '.roxyonignore');\n if (roxyon.length) ig.add(roxyon);\n else ig.add(await readIgnoreFile(root, '.gitignore'));\n return ig;\n}\n\n/** Recursively list files under `root` that survive the ignore matcher (POSIX-relative paths). */\nexport async function listFiles(root: string, ig: Ignore): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const abs = join(dir, entry.name);\n const rel = relative(root, abs).split(sep).join('/');\n if (!rel || ig.ignores(rel) || ig.ignores(`${rel}/`)) continue;\n if (entry.isDirectory()) {\n await walk(abs);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(rel);\n }\n }\n }\n\n await walk(root);\n return files.sort();\n}\n\nexport interface PackResult {\n buffer: Buffer;\n files: string[];\n bytes: number;\n}\n\n/**\n * Create a deterministic gzipped tarball of `root` (minus ignored paths).\n * `prefix`, when given, is prepended to every entry path inside the archive.\n */\nexport async function packDirectory(\n root: string,\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n const ig = await buildIgnore(root);\n const files = await listFiles(root, ig);\n if (files.length === 0) {\n throw new Error(\n `Nothing to deploy from ${root} — every file is ignored or the folder is empty.`,\n );\n }\n\n const stream = tarCreate(\n {\n gzip: true,\n cwd: root,\n portable: true,\n mtime: new Date(0), // stable -> byte-identical archive for an unchanged tree\n prefix: opts.prefix,\n follow: false,\n },\n files,\n );\n\n const chunks: Buffer[] = [];\n let bytes = 0;\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n for await (const chunk of stream as AsyncIterable<Buffer>) {\n bytes += chunk.length;\n if (bytes > max) {\n throw new Error(\n `Deploy archive exceeds ${(max / 1024 / 1024) | 0} MB. Add large files to .roxyonignore.`,\n );\n }\n chunks.push(chunk);\n }\n\n return { buffer: Buffer.concat(chunks), files, bytes };\n}\n\nexport interface FileEntry {\n /** POSIX-style relative path inside the site, e.g. `index.html`, `about/index.html`. */\n path: string;\n /** File body — UTF-8 text, or base64 when `encoding: 'base64'`. */\n content: string;\n encoding?: 'utf8' | 'base64';\n}\n\n/** A path is safe if it is relative, has no `..`/`.` segment, and no NUL. */\nfunction safeEntryPath(p: string): string | null {\n const norm = p.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n if (!norm || norm.startsWith('/') || isAbsolute(norm) || norm.includes('\\0')) return null;\n if (norm.split('/').some((seg) => seg === '..' || seg === '.' || seg === '')) return null;\n return norm;\n}\n\n/**\n * Pack an in-memory list of `{ path, content }` into the same deterministic\n * gzip tarball {@link packDirectory} produces — materialise to a temp dir, then\n * reuse that path so the archive format and determinism are identical. Rejects\n * absolute paths and `..` traversal; `maxBytes` caps the total *decoded* content.\n */\nexport async function packFiles(\n entries: FileEntry[],\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n if (entries.length === 0) throw new Error('No files to deploy.');\n\n const seen = new Set<string>();\n const clean: { path: string; buf: Buffer }[] = [];\n let total = 0;\n for (const e of entries) {\n const rel = safeEntryPath(e.path);\n if (!rel) throw new Error(`Unsafe file path: ${JSON.stringify(e.path)}`);\n if (seen.has(rel)) throw new Error(`Duplicate file path: ${rel}`);\n seen.add(rel);\n const buf = Buffer.from(e.content, e.encoding === 'base64' ? 'base64' : 'utf8');\n total += buf.length;\n clean.push({ path: rel, buf });\n }\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n if (total > max) {\n throw new Error(\n `Content is ${(total / 1024 / 1024).toFixed(1)} MB, which exceeds the ${(max / 1024 / 1024).toFixed(1)} MB limit.`,\n );\n }\n\n const dir = await mkdtemp(join(tmpdir(), 'roxyon-content-'));\n try {\n for (const { path: rel, buf } of clean) {\n const abs = join(dir, rel);\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, buf);\n }\n return await packDirectory(dir, opts);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n}\n\n/** Total on-disk size of a set of files. */\nexport async function measure(root: string, files: string[]): Promise<number> {\n let total = 0;\n for (const f of files) {\n try {\n total += (await stat(join(root, f))).size;\n } catch {\n /* ignore */\n }\n }\n return total;\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { RuntimeName } from '@roxyon/api-client';\n\nexport interface Detection {\n runtime: RuntimeName;\n /** why we think so — shown to the user, who can override */\n reason: string;\n /** best-guess start command for a server runtime */\n start?: string;\n}\n\nasync function exists(root: string, name: string): Promise<boolean> {\n try {\n await readFile(join(root, name));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJson(root: string, name: string): Promise<Record<string, unknown> | undefined> {\n try {\n return JSON.parse(await readFile(join(root, name), 'utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort project-type detection. Deterministic; the caller confirms. */\nexport async function detectRuntime(root: string): Promise<Detection> {\n const pkg = await readJson(root, 'package.json');\n const hasConfigJson = await exists(root, 'config.json');\n let hasViews = false;\n try {\n hasViews = (await readdir(join(root, 'src', 'views'))).length >= 0;\n } catch {\n hasViews = false;\n }\n\n if (hasConfigJson && hasViews) {\n const deps = {\n ...(pkg?.dependencies as object),\n ...(pkg?.devDependencies as object),\n } as Record<string, string>;\n if (!('next' in deps) && !('nuxt' in deps) && !('express' in deps) && !('fastify' in deps)) {\n return { runtime: 'lumen', reason: 'config.json + src/views/ and no server framework' };\n }\n }\n\n if (pkg) {\n const scripts = (pkg.scripts as Record<string, string>) ?? {};\n const start = scripts.start ? 'npm run start' : scripts.serve ? 'npm run serve' : 'node .';\n const deps = { ...(pkg.dependencies as object), ...(pkg.devDependencies as object) } as Record<\n string,\n string\n >;\n if ('next' in deps)\n return { runtime: 'node', reason: 'Next.js in package.json', start: 'npm run start' };\n if ('nuxt' in deps)\n return { runtime: 'node', reason: 'Nuxt in package.json', start: 'npm run start' };\n return { runtime: 'node', reason: 'package.json present', start };\n }\n\n if ((await exists(root, 'requirements.txt')) || (await exists(root, 'pyproject.toml'))) {\n return {\n runtime: 'python',\n reason: 'requirements.txt / pyproject.toml',\n start: 'gunicorn app:app',\n };\n }\n\n if ((await exists(root, 'composer.json')) || (await exists(root, 'server.php'))) {\n return { runtime: 'php', reason: 'composer.json / server.php', start: 'php8.4 server.php' };\n }\n\n return { runtime: 'lumen', reason: 'no framework markers — treating as a static site' };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname, resolve } from 'node:path';\nimport { type DeployKind, LUMEN_BUILD, RUNTIMES, type RuntimeName } from '@roxyon/api-client';\n\nexport type { DeployKind };\n\nexport interface ProjectConfig {\n name: string;\n /** `Applications.objectId`, filled after the first `app`-kind deploy. */\n application?: string;\n /** Route host — `Domains.Name`. */\n host: string;\n /** Sub-path under the host's docroot / source tree. `\"\"` = root. */\n folder: string;\n runtime: RuntimeName;\n runtimeVersion?: string;\n preset?: string;\n kind: DeployKind;\n /** Local build command (LumenJS / static). Empty for server runtimes. */\n build?: string;\n /** Directory the build writes to (relative to project root). */\n outDir?: string;\n /** Web-process start command (app kind only). */\n start?: string;\n /** Serve the app on the host instead of the host's own files. */\n public?: boolean;\n}\n\nexport const PROJECT_FILE = 'roxyon.json';\n\nexport function projectConfigPath(cwd: string = process.cwd()): string {\n return resolve(cwd, PROJECT_FILE);\n}\n\nexport async function loadProjectConfig(\n cwd: string = process.cwd(),\n): Promise<ProjectConfig | undefined> {\n try {\n return JSON.parse(await readFile(projectConfigPath(cwd), 'utf8')) as ProjectConfig;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveProjectConfig(\n config: ProjectConfig,\n cwd: string = process.cwd(),\n): Promise<string> {\n const path = projectConfigPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`);\n return path;\n}\n\nexport interface BuildConfigInput {\n cwd: string;\n runtime: RuntimeName;\n host: string;\n folder?: string;\n name?: string;\n start?: string;\n}\n\n/**\n * Produce a `ProjectConfig` from a resolved runtime + host. Shared by the CLI's\n * `roxyon init` and the MCP `roxyon_init` tool so both write the same shape.\n */\nexport function buildProjectConfig(input: BuildConfigInput): ProjectConfig {\n const folder = String(input.folder ?? '').replace(/^\\/+|\\/+$/g, '');\n const config: ProjectConfig = {\n name: input.name ?? basename(input.cwd),\n host: input.host,\n folder,\n runtime: input.runtime,\n kind: input.runtime === 'lumen' ? 'static' : 'app',\n };\n\n if (input.runtime === 'lumen') {\n config.build = LUMEN_BUILD.command;\n config.outDir = LUMEN_BUILD.outDir;\n } else {\n const spec = RUNTIMES[input.runtime];\n config.runtimeVersion = spec.defaultVersion;\n config.preset = spec.presets[0]?.[0];\n config.start = input.start ?? spec.command;\n config.public = true;\n config.build = '';\n }\n return config;\n}\n","import { access } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n type AccountApp,\n RUNTIMES,\n type Roxyon,\n RoxyonApiError,\n isAuthError,\n} from '@roxyon/api-client';\nimport { packDirectory } from './archive.js';\nimport { type ProjectConfig, loadProjectConfig, saveProjectConfig } from './project.js';\nimport { runShellCommand } from './run-command.js';\n\nexport interface DeployReporter {\n /** A high-level phase change (\"Building\", \"Uploading\", \"Live\"). */\n step?: (msg: string) => void;\n /** A single build- or runtime-log line. */\n log?: (line: string) => void;\n}\n\nexport interface DeployParams {\n cwd: string;\n /** An authenticated client. */\n roxyon: Roxyon;\n /** Subscription objectId / Name to prefer when the account has several. */\n preferredSubscription?: string;\n /** Run the configured build command (default true). */\n build?: boolean;\n /** For app deploys: poll until running/failed (default true). */\n follow?: boolean;\n pollMs?: number;\n timeoutMs?: number;\n reporter?: DeployReporter;\n /** Override the build-command runner (default: spawn a shell). */\n runCommand?: (command: string, cwd: string, reporter?: DeployReporter) => Promise<number>;\n}\n\nexport type DeployOutcome =\n | { kind: 'static'; ok: true; url: string; files: number; config: ProjectConfig }\n | {\n kind: 'app';\n ok: true;\n application: string;\n status: string;\n revision: number;\n url?: string;\n config: ProjectConfig;\n logs: string[];\n }\n | {\n kind: 'app';\n ok: false;\n application: string;\n status: string;\n error: string;\n config: ProjectConfig;\n logs: string[];\n };\n\nexport class DeployError extends Error {\n readonly code:\n | 'no-config'\n | 'auth'\n | 'bad-host'\n | 'build-failed'\n | 'no-build-output'\n | 'upload-failed'\n | 'create-failed';\n constructor(message: string, code: DeployError['code']) {\n super(message);\n this.name = 'DeployError';\n this.code = code;\n }\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Build (if configured), archive, upload, and — for app runtimes — poll until\n * the deploy settles. Self-contained: one `/account/context` call validates the\n * host (works with a session token or a `roxp_` PAT), and the console creates\n * the application on the first deploy — the new id is written back to\n * `roxyon.json`.\n */\nexport async function deployProject(params: DeployParams): Promise<DeployOutcome> {\n const { cwd, roxyon, reporter } = params;\n const runCommand = params.runCommand ?? runShellCommand;\n\n const config = await loadProjectConfig(cwd);\n if (!config) {\n throw new DeployError('No roxyon.json in this directory — run init first.', 'no-config');\n }\n\n // One call — works with a session token or a PAT.\n const ctx = await roxyon.account.context().catch((e) => {\n if (isAuthError(e)) throw new DeployError('Session expired — sign in again.', 'auth');\n throw e;\n });\n const domain = ctx.domains.find((d) => d.name === config.host);\n if (!domain) {\n throw new DeployError(\n `Host \"${config.host}\" is not on this account (have: ${\n ctx.domains.map((d) => d.name).join(', ') || 'none'\n }).`,\n 'bad-host',\n );\n }\n\n // ---- build ----\n if (config.build && params.build !== false) {\n reporter?.step?.(`Build: ${config.build}`);\n const code = await runCommand(config.build, cwd, reporter);\n if (code !== 0) throw new DeployError(`Build command exited ${code}.`, 'build-failed');\n }\n\n // ---- pack ----\n const artifactDir = config.kind === 'static' && config.outDir ? resolve(cwd, config.outDir) : cwd;\n if (config.kind === 'static' && config.outDir) {\n await access(artifactDir).catch(() => {\n throw new DeployError(\n `Build output \"${config.outDir}\" not found — did the build run?`,\n 'no-build-output',\n );\n });\n }\n reporter?.step?.(`Packing ${artifactDir === cwd ? '.' : config.outDir}`);\n const pack = await packDirectory(artifactDir);\n reporter?.log?.(`${pack.files.length} files · ${(pack.bytes / 1024).toFixed(0)} KB`);\n\n // ---- static ----\n if (config.kind === 'static') {\n reporter?.step?.(`Uploading to ${config.host}${config.folder ? `/${config.folder}` : ''}`);\n let res: Awaited<ReturnType<typeof roxyon.sites.deploy>>;\n try {\n res = await roxyon.sites.deploy(config.host, config.folder, pack.buffer);\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n 'upload-failed',\n );\n }\n const url = `https://${res.host}${res.path && res.path !== '/' ? res.path : ''}`;\n reporter?.step?.(`Live — ${url}`);\n return { kind: 'static', ok: true, url, files: res.files ?? pack.files.length, config };\n }\n\n // ---- app ----\n // The console's /applications/deploy creates the app rows on the first deploy\n // (no `application` id yet) from the host + runtime params — one trusted call,\n // so a PAT-only CI job needs no BaaS write access.\n const runtime = config.runtime === 'lumen' ? 'node' : config.runtime;\n const spec = RUNTIMES[runtime];\n const target = config.application ?? {\n host: config.host,\n folder: config.folder,\n runtime,\n runtimeVersion: config.runtimeVersion ?? spec.defaultVersion,\n preset: config.preset ?? spec.presets[0]?.[0] ?? runtime,\n command: config.start ?? spec.command,\n public: config.public ?? true,\n };\n\n reporter?.step?.(\n config.application ? 'Uploading source' : 'Creating the application & uploading',\n );\n let revision = 0;\n let appId: string;\n try {\n const res = await roxyon.applications.uploadSource(target, pack.buffer);\n revision = res.configRevision ?? 0;\n appId = res.application;\n if (res.created && appId && appId !== config.application) {\n config.application = appId;\n await saveProjectConfig(config, cwd);\n reporter?.log?.(`application ${appId} created`);\n }\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n config.application ? 'upload-failed' : 'create-failed',\n );\n }\n\n const url = config.public\n ? `https://${config.host}${config.folder ? `/${config.folder}` : ''}`\n : undefined;\n\n if (params.follow === false) {\n reporter?.step?.(`Build queued (revision ${revision})`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'building',\n revision,\n url,\n config,\n logs: [],\n };\n }\n\n return watch(roxyon, appId, {\n reporter,\n url,\n config,\n pollMs: params.pollMs ?? 3000,\n timeoutMs: params.timeoutMs ?? 10 * 60 * 1000,\n });\n}\n\nasync function watch(\n roxyon: Roxyon,\n appId: string,\n opts: {\n reporter?: DeployReporter;\n url?: string;\n config: ProjectConfig;\n pollMs: number;\n timeoutMs: number;\n },\n): Promise<DeployOutcome> {\n const started = Date.now();\n const seen = new Set<string>();\n const collected: string[] = [];\n let lastStatus = '';\n\n opts.reporter?.step?.('Building');\n while (true) {\n await sleep(opts.pollMs);\n\n let app: AccountApp | undefined;\n try {\n app = await roxyon.account.getApp(appId);\n } catch {\n /* transient */\n }\n if (!app) {\n if (Date.now() - started > opts.timeoutMs) break;\n continue;\n }\n\n const cfg = app.configRevision;\n const applied = app.appliedRevision;\n const status = app.status;\n if (status !== lastStatus) {\n opts.reporter?.step?.(`${status || 'pending'} (${applied}/${cfg})`);\n lastStatus = status;\n }\n\n try {\n for (const line of await roxyon.applications.logs(appId, 40)) {\n if (line && !seen.has(line)) {\n seen.add(line);\n collected.push(line);\n opts.reporter?.log?.(line);\n }\n }\n } catch {\n /* transient */\n }\n\n if (cfg <= applied && status === 'running') {\n opts.reporter?.step?.(`Live${opts.url ? ` — ${opts.url}` : ''}`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'running',\n revision: applied,\n url: opts.url,\n config: opts.config,\n logs: collected,\n };\n }\n if (status === 'failed') {\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'failed',\n error: (app.lastError || 'Deploy failed.').trim(),\n config: opts.config,\n logs: collected,\n };\n }\n if (Date.now() - started > opts.timeoutMs) break;\n }\n\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'timeout',\n error: 'Still building after the timeout — check `roxyon logs`.',\n config: opts.config,\n logs: collected,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { DeployReporter } from './deploy.js';\n\n/**\n * Default build-command runner: spawn a shell, stream output to the reporter.\n * Callers in a non-local context (a hosted server) can pass their own.\n */\nexport function runShellCommand(\n command: string,\n cwd: string,\n reporter?: DeployReporter,\n): Promise<number> {\n return new Promise((resolvePromise) => {\n const child = spawn(command, { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n const pipe = (buf: Buffer) => {\n for (const line of buf.toString('utf8').split('\\n')) {\n if (line.trim()) reporter?.log?.(line);\n }\n };\n child.stdout?.on('data', pipe);\n child.stderr?.on('data', pipe);\n child.on('close', (code) => resolvePromise(code ?? 0));\n child.on('error', () => resolvePromise(127));\n });\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport { extract } from 'tar';\n\nconst WORDPRESS_TARBALL_URL = 'https://wordpress.org/latest.tar.gz';\nconst WORDPRESS_SALTS_URL = 'https://api.wordpress.org/secret-key/1.1/salt/';\nconst PLACEHOLDER = 'put your unique phrase here';\n\nexport interface WordPressDbCredentials {\n name: string;\n username: string;\n password: string;\n /** `host:port`, e.g. `10.0.0.2:6033` — WordPress's `DB_HOST` accepts this form directly. */\n host: string;\n}\n\nexport interface InstallWordPressOptions {\n /** Destination directory — created if missing. Must be empty or not yet exist. */\n dir: string;\n db: WordPressDbCredentials;\n /** Default `wp_`. */\n tablePrefix?: string;\n /** Override for tests — must resolve like the global `fetch`. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface InstallWordPressResult {\n dir: string;\n}\n\n/**\n * Download WordPress core into `dir` and write a working `wp-config.php`\n * against the given database. Does not touch the network beyond\n * wordpress.org/api.wordpress.org, and does not deploy anywhere — pack `dir`\n * with {@link packDirectory} and upload with `roxyon.sites.deploy()` (the same\n * tar path `roxyon deploy` uses for static/app content; WordPress core is\n * thousands of files, well past the small JSON `roxyon_deploy_content` tool's\n * budget).\n */\nexport async function installWordPress(\n opts: InstallWordPressOptions,\n): Promise<InstallWordPressResult> {\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n await mkdir(opts.dir, { recursive: true });\n\n const coreRes = await fetchImpl(WORDPRESS_TARBALL_URL);\n if (!coreRes.ok) {\n throw new Error(`Could not download WordPress core (HTTP ${coreRes.status}).`);\n }\n const coreBuf = Buffer.from(await coreRes.arrayBuffer());\n\n // The official tarball has one top-level \"wordpress/\" directory — strip it\n // so core files land directly in `dir`.\n await new Promise<void>((resolvePromise, reject) => {\n const writer = extract({ cwd: opts.dir, strip: 1 });\n Readable.from(coreBuf)\n .pipe(writer)\n .on('finish', () => resolvePromise())\n .on('error', reject);\n });\n\n const saltsRes = await fetchImpl(WORDPRESS_SALTS_URL);\n const salts = saltsRes.ok ? (await saltsRes.text()).trim() : '';\n\n const samplePath = `${opts.dir}/wp-config-sample.php`;\n const sample = await readFile(samplePath, 'utf8');\n const config = buildWpConfig(sample, opts.db, opts.tablePrefix, salts);\n await writeFile(`${opts.dir}/wp-config.php`, config, 'utf8');\n\n return { dir: opts.dir };\n}\n\n/** Pure string transform — exported separately so it's testable without any network. */\nexport function buildWpConfig(\n sample: string,\n db: WordPressDbCredentials,\n tablePrefix = 'wp_',\n salts = '',\n): string {\n let out = sample\n .replace(/(['\"])database_name_here\\1/, `$1${escapePhp(db.name)}$1`)\n .replace(/(['\"])username_here\\1/, `$1${escapePhp(db.username)}$1`)\n .replace(/(['\"])password_here\\1/, `$1${escapePhp(db.password)}$1`)\n .replace(/(define\\(\\s*['\"]DB_HOST['\"]\\s*,\\s*)(['\"])localhost\\2/, `$1$2${escapePhp(db.host)}$2`)\n .replace(/(\\$table_prefix\\s*=\\s*)(['\"])wp_\\2/, `$1$2${escapePhp(tablePrefix)}$2`);\n\n if (salts) {\n const lines = out.split('\\n');\n const start = lines.findIndex((l) => l.includes(PLACEHOLDER));\n if (start !== -1) {\n let end = start;\n while (end < lines.length && lines[end]!.includes(PLACEHOLDER)) end++;\n lines.splice(start, end - start, salts);\n out = lines.join('\\n');\n }\n }\n\n return out;\n}\n\nfunction escapePhp(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written\n * by `roxyon login`; read by the CLI and the MCP server alike.\n */\nexport interface Credentials {\n sessionToken: string;\n refreshToken?: string;\n userId: string;\n email?: string;\n /** Preferred subscription (objectId / Name) when the account has several. */\n subscription?: string;\n savedAt: string;\n}\n\nexport interface Endpoints {\n apiUrl?: string;\n consoleUrl?: string;\n}\n\nexport function credentialsDir(): string {\n return join(homedir(), '.roxyon');\n}\nexport function credentialsPath(): string {\n return join(credentialsDir(), 'config.json');\n}\n\n/** Endpoint overrides from the environment (`ROXYON_API_URL`, `ROXYON_CONSOLE_URL`). */\nexport function endpointsFromEnv(): Endpoints {\n return {\n apiUrl: process.env.ROXYON_API_URL,\n consoleUrl: process.env.ROXYON_CONSOLE_URL,\n };\n}\n\n/** A token supplied out-of-band (CI, or an MCP host env). Skips the stored login. */\nexport function tokenFromEnv(): string | undefined {\n return process.env.ROXYON_TOKEN || undefined;\n}\n\nexport async function loadCredentials(): Promise<Credentials | undefined> {\n try {\n return JSON.parse(await readFile(credentialsPath(), 'utf8')) as Credentials;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveCredentials(creds: Credentials): Promise<string> {\n const path = credentialsPath();\n await mkdir(credentialsDir(), { recursive: true, mode: 0o700 });\n await writeFile(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600).catch(() => undefined);\n return path;\n}\n\nexport async function clearCredentials(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n"],"mappings":"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","names":["ignore","tarCreate","import_promises","import_node_path","import_promises","import_node_path","import_promises","import_node_path","import_api_client","url","import_promises","import_tar","import_promises","import_node_os","import_node_path"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -21,6 +21,23 @@ declare function packDirectory(root: string, opts?: {
|
|
|
21
21
|
prefix?: string;
|
|
22
22
|
maxBytes?: number;
|
|
23
23
|
}): Promise<PackResult>;
|
|
24
|
+
interface FileEntry {
|
|
25
|
+
/** POSIX-style relative path inside the site, e.g. `index.html`, `about/index.html`. */
|
|
26
|
+
path: string;
|
|
27
|
+
/** File body — UTF-8 text, or base64 when `encoding: 'base64'`. */
|
|
28
|
+
content: string;
|
|
29
|
+
encoding?: 'utf8' | 'base64';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pack an in-memory list of `{ path, content }` into the same deterministic
|
|
33
|
+
* gzip tarball {@link packDirectory} produces — materialise to a temp dir, then
|
|
34
|
+
* reuse that path so the archive format and determinism are identical. Rejects
|
|
35
|
+
* absolute paths and `..` traversal; `maxBytes` caps the total *decoded* content.
|
|
36
|
+
*/
|
|
37
|
+
declare function packFiles(entries: FileEntry[], opts?: {
|
|
38
|
+
prefix?: string;
|
|
39
|
+
maxBytes?: number;
|
|
40
|
+
}): Promise<PackResult>;
|
|
24
41
|
/** Total on-disk size of a set of files. */
|
|
25
42
|
declare function measure(root: string, files: string[]): Promise<number>;
|
|
26
43
|
|
|
@@ -138,6 +155,38 @@ declare function deployProject(params: DeployParams): Promise<DeployOutcome>;
|
|
|
138
155
|
*/
|
|
139
156
|
declare function runShellCommand(command: string, cwd: string, reporter?: DeployReporter): Promise<number>;
|
|
140
157
|
|
|
158
|
+
interface WordPressDbCredentials {
|
|
159
|
+
name: string;
|
|
160
|
+
username: string;
|
|
161
|
+
password: string;
|
|
162
|
+
/** `host:port`, e.g. `10.0.0.2:6033` — WordPress's `DB_HOST` accepts this form directly. */
|
|
163
|
+
host: string;
|
|
164
|
+
}
|
|
165
|
+
interface InstallWordPressOptions {
|
|
166
|
+
/** Destination directory — created if missing. Must be empty or not yet exist. */
|
|
167
|
+
dir: string;
|
|
168
|
+
db: WordPressDbCredentials;
|
|
169
|
+
/** Default `wp_`. */
|
|
170
|
+
tablePrefix?: string;
|
|
171
|
+
/** Override for tests — must resolve like the global `fetch`. */
|
|
172
|
+
fetchImpl?: typeof fetch;
|
|
173
|
+
}
|
|
174
|
+
interface InstallWordPressResult {
|
|
175
|
+
dir: string;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Download WordPress core into `dir` and write a working `wp-config.php`
|
|
179
|
+
* against the given database. Does not touch the network beyond
|
|
180
|
+
* wordpress.org/api.wordpress.org, and does not deploy anywhere — pack `dir`
|
|
181
|
+
* with {@link packDirectory} and upload with `roxyon.sites.deploy()` (the same
|
|
182
|
+
* tar path `roxyon deploy` uses for static/app content; WordPress core is
|
|
183
|
+
* thousands of files, well past the small JSON `roxyon_deploy_content` tool's
|
|
184
|
+
* budget).
|
|
185
|
+
*/
|
|
186
|
+
declare function installWordPress(opts: InstallWordPressOptions): Promise<InstallWordPressResult>;
|
|
187
|
+
/** Pure string transform — exported separately so it's testable without any network. */
|
|
188
|
+
declare function buildWpConfig(sample: string, db: WordPressDbCredentials, tablePrefix?: string, salts?: string): string;
|
|
189
|
+
|
|
141
190
|
/**
|
|
142
191
|
* Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written
|
|
143
192
|
* by `roxyon login`; read by the CLI and the MCP server alike.
|
|
@@ -165,4 +214,4 @@ declare function loadCredentials(): Promise<Credentials | undefined>;
|
|
|
165
214
|
declare function saveCredentials(creds: Credentials): Promise<string>;
|
|
166
215
|
declare function clearCredentials(): Promise<void>;
|
|
167
216
|
|
|
168
|
-
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, PROJECT_FILE, type PackResult, type ProjectConfig, buildIgnore, buildProjectConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, listFiles, loadCredentials, loadProjectConfig, measure, packDirectory, projectConfigPath, runShellCommand, saveCredentials, saveProjectConfig, tokenFromEnv };
|
|
217
|
+
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, type FileEntry, type InstallWordPressOptions, type InstallWordPressResult, PROJECT_FILE, type PackResult, type ProjectConfig, type WordPressDbCredentials, buildIgnore, buildProjectConfig, buildWpConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, installWordPress, listFiles, loadCredentials, loadProjectConfig, measure, packDirectory, packFiles, projectConfigPath, runShellCommand, saveCredentials, saveProjectConfig, tokenFromEnv };
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,23 @@ declare function packDirectory(root: string, opts?: {
|
|
|
21
21
|
prefix?: string;
|
|
22
22
|
maxBytes?: number;
|
|
23
23
|
}): Promise<PackResult>;
|
|
24
|
+
interface FileEntry {
|
|
25
|
+
/** POSIX-style relative path inside the site, e.g. `index.html`, `about/index.html`. */
|
|
26
|
+
path: string;
|
|
27
|
+
/** File body — UTF-8 text, or base64 when `encoding: 'base64'`. */
|
|
28
|
+
content: string;
|
|
29
|
+
encoding?: 'utf8' | 'base64';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Pack an in-memory list of `{ path, content }` into the same deterministic
|
|
33
|
+
* gzip tarball {@link packDirectory} produces — materialise to a temp dir, then
|
|
34
|
+
* reuse that path so the archive format and determinism are identical. Rejects
|
|
35
|
+
* absolute paths and `..` traversal; `maxBytes` caps the total *decoded* content.
|
|
36
|
+
*/
|
|
37
|
+
declare function packFiles(entries: FileEntry[], opts?: {
|
|
38
|
+
prefix?: string;
|
|
39
|
+
maxBytes?: number;
|
|
40
|
+
}): Promise<PackResult>;
|
|
24
41
|
/** Total on-disk size of a set of files. */
|
|
25
42
|
declare function measure(root: string, files: string[]): Promise<number>;
|
|
26
43
|
|
|
@@ -138,6 +155,38 @@ declare function deployProject(params: DeployParams): Promise<DeployOutcome>;
|
|
|
138
155
|
*/
|
|
139
156
|
declare function runShellCommand(command: string, cwd: string, reporter?: DeployReporter): Promise<number>;
|
|
140
157
|
|
|
158
|
+
interface WordPressDbCredentials {
|
|
159
|
+
name: string;
|
|
160
|
+
username: string;
|
|
161
|
+
password: string;
|
|
162
|
+
/** `host:port`, e.g. `10.0.0.2:6033` — WordPress's `DB_HOST` accepts this form directly. */
|
|
163
|
+
host: string;
|
|
164
|
+
}
|
|
165
|
+
interface InstallWordPressOptions {
|
|
166
|
+
/** Destination directory — created if missing. Must be empty or not yet exist. */
|
|
167
|
+
dir: string;
|
|
168
|
+
db: WordPressDbCredentials;
|
|
169
|
+
/** Default `wp_`. */
|
|
170
|
+
tablePrefix?: string;
|
|
171
|
+
/** Override for tests — must resolve like the global `fetch`. */
|
|
172
|
+
fetchImpl?: typeof fetch;
|
|
173
|
+
}
|
|
174
|
+
interface InstallWordPressResult {
|
|
175
|
+
dir: string;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Download WordPress core into `dir` and write a working `wp-config.php`
|
|
179
|
+
* against the given database. Does not touch the network beyond
|
|
180
|
+
* wordpress.org/api.wordpress.org, and does not deploy anywhere — pack `dir`
|
|
181
|
+
* with {@link packDirectory} and upload with `roxyon.sites.deploy()` (the same
|
|
182
|
+
* tar path `roxyon deploy` uses for static/app content; WordPress core is
|
|
183
|
+
* thousands of files, well past the small JSON `roxyon_deploy_content` tool's
|
|
184
|
+
* budget).
|
|
185
|
+
*/
|
|
186
|
+
declare function installWordPress(opts: InstallWordPressOptions): Promise<InstallWordPressResult>;
|
|
187
|
+
/** Pure string transform — exported separately so it's testable without any network. */
|
|
188
|
+
declare function buildWpConfig(sample: string, db: WordPressDbCredentials, tablePrefix?: string, salts?: string): string;
|
|
189
|
+
|
|
141
190
|
/**
|
|
142
191
|
* Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written
|
|
143
192
|
* by `roxyon login`; read by the CLI and the MCP server alike.
|
|
@@ -165,4 +214,4 @@ declare function loadCredentials(): Promise<Credentials | undefined>;
|
|
|
165
214
|
declare function saveCredentials(creds: Credentials): Promise<string>;
|
|
166
215
|
declare function clearCredentials(): Promise<void>;
|
|
167
216
|
|
|
168
|
-
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, PROJECT_FILE, type PackResult, type ProjectConfig, buildIgnore, buildProjectConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, listFiles, loadCredentials, loadProjectConfig, measure, packDirectory, projectConfigPath, runShellCommand, saveCredentials, saveProjectConfig, tokenFromEnv };
|
|
217
|
+
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, type FileEntry, type InstallWordPressOptions, type InstallWordPressResult, PROJECT_FILE, type PackResult, type ProjectConfig, type WordPressDbCredentials, buildIgnore, buildProjectConfig, buildWpConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, installWordPress, listFiles, loadCredentials, loadProjectConfig, measure, packDirectory, packFiles, projectConfigPath, runShellCommand, saveCredentials, saveProjectConfig, tokenFromEnv };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/archive.ts
|
|
2
|
-
import { readFile, readdir, stat } from "fs/promises";
|
|
3
|
-
import {
|
|
2
|
+
import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { dirname, isAbsolute, join, relative, sep } from "path";
|
|
4
5
|
import ignore from "ignore";
|
|
5
6
|
import { create as tarCreate } from "tar";
|
|
6
7
|
var ALWAYS_IGNORE = [
|
|
@@ -80,6 +81,44 @@ async function packDirectory(root, opts = {}) {
|
|
|
80
81
|
}
|
|
81
82
|
return { buffer: Buffer.concat(chunks), files, bytes };
|
|
82
83
|
}
|
|
84
|
+
function safeEntryPath(p) {
|
|
85
|
+
const norm = p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
86
|
+
if (!norm || norm.startsWith("/") || isAbsolute(norm) || norm.includes("\0")) return null;
|
|
87
|
+
if (norm.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) return null;
|
|
88
|
+
return norm;
|
|
89
|
+
}
|
|
90
|
+
async function packFiles(entries, opts = {}) {
|
|
91
|
+
if (entries.length === 0) throw new Error("No files to deploy.");
|
|
92
|
+
const seen = /* @__PURE__ */ new Set();
|
|
93
|
+
const clean = [];
|
|
94
|
+
let total = 0;
|
|
95
|
+
for (const e of entries) {
|
|
96
|
+
const rel = safeEntryPath(e.path);
|
|
97
|
+
if (!rel) throw new Error(`Unsafe file path: ${JSON.stringify(e.path)}`);
|
|
98
|
+
if (seen.has(rel)) throw new Error(`Duplicate file path: ${rel}`);
|
|
99
|
+
seen.add(rel);
|
|
100
|
+
const buf = Buffer.from(e.content, e.encoding === "base64" ? "base64" : "utf8");
|
|
101
|
+
total += buf.length;
|
|
102
|
+
clean.push({ path: rel, buf });
|
|
103
|
+
}
|
|
104
|
+
const max = opts.maxBytes ?? 512 * 1024 * 1024;
|
|
105
|
+
if (total > max) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Content is ${(total / 1024 / 1024).toFixed(1)} MB, which exceeds the ${(max / 1024 / 1024).toFixed(1)} MB limit.`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const dir = await mkdtemp(join(tmpdir(), "roxyon-content-"));
|
|
111
|
+
try {
|
|
112
|
+
for (const { path: rel, buf } of clean) {
|
|
113
|
+
const abs = join(dir, rel);
|
|
114
|
+
await mkdir(dirname(abs), { recursive: true });
|
|
115
|
+
await writeFile(abs, buf);
|
|
116
|
+
}
|
|
117
|
+
return await packDirectory(dir, opts);
|
|
118
|
+
} finally {
|
|
119
|
+
await rm(dir, { recursive: true, force: true });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
83
122
|
async function measure(root, files) {
|
|
84
123
|
let total = 0;
|
|
85
124
|
for (const f of files) {
|
|
@@ -151,8 +190,8 @@ async function detectRuntime(root) {
|
|
|
151
190
|
}
|
|
152
191
|
|
|
153
192
|
// src/project.ts
|
|
154
|
-
import { mkdir, readFile as readFile3, writeFile } from "fs/promises";
|
|
155
|
-
import { basename, dirname, resolve } from "path";
|
|
193
|
+
import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
194
|
+
import { basename, dirname as dirname2, resolve } from "path";
|
|
156
195
|
import { LUMEN_BUILD, RUNTIMES } from "@roxyon/api-client";
|
|
157
196
|
var PROJECT_FILE = "roxyon.json";
|
|
158
197
|
function projectConfigPath(cwd = process.cwd()) {
|
|
@@ -167,8 +206,8 @@ async function loadProjectConfig(cwd = process.cwd()) {
|
|
|
167
206
|
}
|
|
168
207
|
async function saveProjectConfig(config, cwd = process.cwd()) {
|
|
169
208
|
const path = projectConfigPath(cwd);
|
|
170
|
-
await
|
|
171
|
-
await
|
|
209
|
+
await mkdir2(dirname2(path), { recursive: true });
|
|
210
|
+
await writeFile2(path, `${JSON.stringify(config, null, 2)}
|
|
172
211
|
`);
|
|
173
212
|
return path;
|
|
174
213
|
}
|
|
@@ -405,8 +444,53 @@ async function watch(roxyon, appId, opts) {
|
|
|
405
444
|
};
|
|
406
445
|
}
|
|
407
446
|
|
|
447
|
+
// src/wordpress.ts
|
|
448
|
+
import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
449
|
+
import { Readable } from "stream";
|
|
450
|
+
import { extract } from "tar";
|
|
451
|
+
var WORDPRESS_TARBALL_URL = "https://wordpress.org/latest.tar.gz";
|
|
452
|
+
var WORDPRESS_SALTS_URL = "https://api.wordpress.org/secret-key/1.1/salt/";
|
|
453
|
+
var PLACEHOLDER = "put your unique phrase here";
|
|
454
|
+
async function installWordPress(opts) {
|
|
455
|
+
const fetchImpl = opts.fetchImpl ?? globalThis.fetch;
|
|
456
|
+
await mkdir3(opts.dir, { recursive: true });
|
|
457
|
+
const coreRes = await fetchImpl(WORDPRESS_TARBALL_URL);
|
|
458
|
+
if (!coreRes.ok) {
|
|
459
|
+
throw new Error(`Could not download WordPress core (HTTP ${coreRes.status}).`);
|
|
460
|
+
}
|
|
461
|
+
const coreBuf = Buffer.from(await coreRes.arrayBuffer());
|
|
462
|
+
await new Promise((resolvePromise, reject) => {
|
|
463
|
+
const writer = extract({ cwd: opts.dir, strip: 1 });
|
|
464
|
+
Readable.from(coreBuf).pipe(writer).on("finish", () => resolvePromise()).on("error", reject);
|
|
465
|
+
});
|
|
466
|
+
const saltsRes = await fetchImpl(WORDPRESS_SALTS_URL);
|
|
467
|
+
const salts = saltsRes.ok ? (await saltsRes.text()).trim() : "";
|
|
468
|
+
const samplePath = `${opts.dir}/wp-config-sample.php`;
|
|
469
|
+
const sample = await readFile4(samplePath, "utf8");
|
|
470
|
+
const config = buildWpConfig(sample, opts.db, opts.tablePrefix, salts);
|
|
471
|
+
await writeFile3(`${opts.dir}/wp-config.php`, config, "utf8");
|
|
472
|
+
return { dir: opts.dir };
|
|
473
|
+
}
|
|
474
|
+
function buildWpConfig(sample, db, tablePrefix = "wp_", salts = "") {
|
|
475
|
+
let out = sample.replace(/(['"])database_name_here\1/, `$1${escapePhp(db.name)}$1`).replace(/(['"])username_here\1/, `$1${escapePhp(db.username)}$1`).replace(/(['"])password_here\1/, `$1${escapePhp(db.password)}$1`).replace(/(define\(\s*['"]DB_HOST['"]\s*,\s*)(['"])localhost\2/, `$1$2${escapePhp(db.host)}$2`).replace(/(\$table_prefix\s*=\s*)(['"])wp_\2/, `$1$2${escapePhp(tablePrefix)}$2`);
|
|
476
|
+
if (salts) {
|
|
477
|
+
const lines = out.split("\n");
|
|
478
|
+
const start = lines.findIndex((l) => l.includes(PLACEHOLDER));
|
|
479
|
+
if (start !== -1) {
|
|
480
|
+
let end = start;
|
|
481
|
+
while (end < lines.length && lines[end].includes(PLACEHOLDER)) end++;
|
|
482
|
+
lines.splice(start, end - start, salts);
|
|
483
|
+
out = lines.join("\n");
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return out;
|
|
487
|
+
}
|
|
488
|
+
function escapePhp(s) {
|
|
489
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
490
|
+
}
|
|
491
|
+
|
|
408
492
|
// src/credentials.ts
|
|
409
|
-
import { chmod, mkdir as
|
|
493
|
+
import { chmod, mkdir as mkdir4, readFile as readFile5, rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
410
494
|
import { homedir } from "os";
|
|
411
495
|
import { join as join3 } from "path";
|
|
412
496
|
function credentialsDir() {
|
|
@@ -426,21 +510,21 @@ function tokenFromEnv() {
|
|
|
426
510
|
}
|
|
427
511
|
async function loadCredentials() {
|
|
428
512
|
try {
|
|
429
|
-
return JSON.parse(await
|
|
513
|
+
return JSON.parse(await readFile5(credentialsPath(), "utf8"));
|
|
430
514
|
} catch {
|
|
431
515
|
return void 0;
|
|
432
516
|
}
|
|
433
517
|
}
|
|
434
518
|
async function saveCredentials(creds) {
|
|
435
519
|
const path = credentialsPath();
|
|
436
|
-
await
|
|
437
|
-
await
|
|
520
|
+
await mkdir4(credentialsDir(), { recursive: true, mode: 448 });
|
|
521
|
+
await writeFile4(path, `${JSON.stringify(creds, null, 2)}
|
|
438
522
|
`, { mode: 384 });
|
|
439
523
|
await chmod(path, 384).catch(() => void 0);
|
|
440
524
|
return path;
|
|
441
525
|
}
|
|
442
526
|
async function clearCredentials() {
|
|
443
|
-
await
|
|
527
|
+
await rm2(credentialsPath(), { force: true });
|
|
444
528
|
}
|
|
445
529
|
export {
|
|
446
530
|
ALWAYS_IGNORE,
|
|
@@ -448,17 +532,20 @@ export {
|
|
|
448
532
|
PROJECT_FILE,
|
|
449
533
|
buildIgnore,
|
|
450
534
|
buildProjectConfig,
|
|
535
|
+
buildWpConfig,
|
|
451
536
|
clearCredentials,
|
|
452
537
|
credentialsDir,
|
|
453
538
|
credentialsPath,
|
|
454
539
|
deployProject,
|
|
455
540
|
detectRuntime,
|
|
456
541
|
endpointsFromEnv,
|
|
542
|
+
installWordPress,
|
|
457
543
|
listFiles,
|
|
458
544
|
loadCredentials,
|
|
459
545
|
loadProjectConfig,
|
|
460
546
|
measure,
|
|
461
547
|
packDirectory,
|
|
548
|
+
packFiles,
|
|
462
549
|
projectConfigPath,
|
|
463
550
|
runShellCommand,
|
|
464
551
|
saveCredentials,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/archive.ts","../src/detect.ts","../src/project.ts","../src/deploy.ts","../src/run-command.ts","../src/credentials.ts"],"sourcesContent":["import { readFile, readdir, stat } from 'node:fs/promises';\nimport { join, relative, sep } from 'node:path';\nimport ignore, { type Ignore } from 'ignore';\nimport { create as tarCreate } from 'tar';\n\n/** Always excluded, regardless of ignore files — build artefacts and VCS/OS cruft. */\nexport const ALWAYS_IGNORE = [\n 'node_modules',\n '.git',\n '.next',\n '.nuxt',\n '.venv',\n '__pycache__',\n '.DS_Store',\n '.roxyon',\n 'dist/.cache',\n];\n\nasync function readIgnoreFile(root: string, name: string): Promise<string[]> {\n try {\n return (await readFile(join(root, name), 'utf8')).split(/\\r?\\n/);\n } catch {\n return [];\n }\n}\n\n/** Build the ignore matcher: `.roxyonignore` if present, else `.gitignore`, plus the always-list. */\nexport async function buildIgnore(root: string): Promise<Ignore> {\n const ig = ignore().add(ALWAYS_IGNORE);\n const roxyon = await readIgnoreFile(root, '.roxyonignore');\n if (roxyon.length) ig.add(roxyon);\n else ig.add(await readIgnoreFile(root, '.gitignore'));\n return ig;\n}\n\n/** Recursively list files under `root` that survive the ignore matcher (POSIX-relative paths). */\nexport async function listFiles(root: string, ig: Ignore): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const abs = join(dir, entry.name);\n const rel = relative(root, abs).split(sep).join('/');\n if (!rel || ig.ignores(rel) || ig.ignores(`${rel}/`)) continue;\n if (entry.isDirectory()) {\n await walk(abs);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(rel);\n }\n }\n }\n\n await walk(root);\n return files.sort();\n}\n\nexport interface PackResult {\n buffer: Buffer;\n files: string[];\n bytes: number;\n}\n\n/**\n * Create a deterministic gzipped tarball of `root` (minus ignored paths).\n * `prefix`, when given, is prepended to every entry path inside the archive.\n */\nexport async function packDirectory(\n root: string,\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n const ig = await buildIgnore(root);\n const files = await listFiles(root, ig);\n if (files.length === 0) {\n throw new Error(\n `Nothing to deploy from ${root} — every file is ignored or the folder is empty.`,\n );\n }\n\n const stream = tarCreate(\n {\n gzip: true,\n cwd: root,\n portable: true,\n mtime: new Date(0), // stable -> byte-identical archive for an unchanged tree\n prefix: opts.prefix,\n follow: false,\n },\n files,\n );\n\n const chunks: Buffer[] = [];\n let bytes = 0;\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n for await (const chunk of stream as AsyncIterable<Buffer>) {\n bytes += chunk.length;\n if (bytes > max) {\n throw new Error(\n `Deploy archive exceeds ${(max / 1024 / 1024) | 0} MB. Add large files to .roxyonignore.`,\n );\n }\n chunks.push(chunk);\n }\n\n return { buffer: Buffer.concat(chunks), files, bytes };\n}\n\n/** Total on-disk size of a set of files. */\nexport async function measure(root: string, files: string[]): Promise<number> {\n let total = 0;\n for (const f of files) {\n try {\n total += (await stat(join(root, f))).size;\n } catch {\n /* ignore */\n }\n }\n return total;\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { RuntimeName } from '@roxyon/api-client';\n\nexport interface Detection {\n runtime: RuntimeName;\n /** why we think so — shown to the user, who can override */\n reason: string;\n /** best-guess start command for a server runtime */\n start?: string;\n}\n\nasync function exists(root: string, name: string): Promise<boolean> {\n try {\n await readFile(join(root, name));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJson(root: string, name: string): Promise<Record<string, unknown> | undefined> {\n try {\n return JSON.parse(await readFile(join(root, name), 'utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort project-type detection. Deterministic; the caller confirms. */\nexport async function detectRuntime(root: string): Promise<Detection> {\n const pkg = await readJson(root, 'package.json');\n const hasConfigJson = await exists(root, 'config.json');\n let hasViews = false;\n try {\n hasViews = (await readdir(join(root, 'src', 'views'))).length >= 0;\n } catch {\n hasViews = false;\n }\n\n if (hasConfigJson && hasViews) {\n const deps = {\n ...(pkg?.dependencies as object),\n ...(pkg?.devDependencies as object),\n } as Record<string, string>;\n if (!('next' in deps) && !('nuxt' in deps) && !('express' in deps) && !('fastify' in deps)) {\n return { runtime: 'lumen', reason: 'config.json + src/views/ and no server framework' };\n }\n }\n\n if (pkg) {\n const scripts = (pkg.scripts as Record<string, string>) ?? {};\n const start = scripts.start ? 'npm run start' : scripts.serve ? 'npm run serve' : 'node .';\n const deps = { ...(pkg.dependencies as object), ...(pkg.devDependencies as object) } as Record<\n string,\n string\n >;\n if ('next' in deps)\n return { runtime: 'node', reason: 'Next.js in package.json', start: 'npm run start' };\n if ('nuxt' in deps)\n return { runtime: 'node', reason: 'Nuxt in package.json', start: 'npm run start' };\n return { runtime: 'node', reason: 'package.json present', start };\n }\n\n if ((await exists(root, 'requirements.txt')) || (await exists(root, 'pyproject.toml'))) {\n return {\n runtime: 'python',\n reason: 'requirements.txt / pyproject.toml',\n start: 'gunicorn app:app',\n };\n }\n\n if ((await exists(root, 'composer.json')) || (await exists(root, 'server.php'))) {\n return { runtime: 'php', reason: 'composer.json / server.php', start: 'php8.4 server.php' };\n }\n\n return { runtime: 'lumen', reason: 'no framework markers — treating as a static site' };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname, resolve } from 'node:path';\nimport { type DeployKind, LUMEN_BUILD, RUNTIMES, type RuntimeName } from '@roxyon/api-client';\n\nexport type { DeployKind };\n\nexport interface ProjectConfig {\n name: string;\n /** `Applications.objectId`, filled after the first `app`-kind deploy. */\n application?: string;\n /** Route host — `Domains.Name`. */\n host: string;\n /** Sub-path under the host's docroot / source tree. `\"\"` = root. */\n folder: string;\n runtime: RuntimeName;\n runtimeVersion?: string;\n preset?: string;\n kind: DeployKind;\n /** Local build command (LumenJS / static). Empty for server runtimes. */\n build?: string;\n /** Directory the build writes to (relative to project root). */\n outDir?: string;\n /** Web-process start command (app kind only). */\n start?: string;\n /** Serve the app on the host instead of the host's own files. */\n public?: boolean;\n}\n\nexport const PROJECT_FILE = 'roxyon.json';\n\nexport function projectConfigPath(cwd: string = process.cwd()): string {\n return resolve(cwd, PROJECT_FILE);\n}\n\nexport async function loadProjectConfig(\n cwd: string = process.cwd(),\n): Promise<ProjectConfig | undefined> {\n try {\n return JSON.parse(await readFile(projectConfigPath(cwd), 'utf8')) as ProjectConfig;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveProjectConfig(\n config: ProjectConfig,\n cwd: string = process.cwd(),\n): Promise<string> {\n const path = projectConfigPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`);\n return path;\n}\n\nexport interface BuildConfigInput {\n cwd: string;\n runtime: RuntimeName;\n host: string;\n folder?: string;\n name?: string;\n start?: string;\n}\n\n/**\n * Produce a `ProjectConfig` from a resolved runtime + host. Shared by the CLI's\n * `roxyon init` and the MCP `roxyon_init` tool so both write the same shape.\n */\nexport function buildProjectConfig(input: BuildConfigInput): ProjectConfig {\n const folder = String(input.folder ?? '').replace(/^\\/+|\\/+$/g, '');\n const config: ProjectConfig = {\n name: input.name ?? basename(input.cwd),\n host: input.host,\n folder,\n runtime: input.runtime,\n kind: input.runtime === 'lumen' ? 'static' : 'app',\n };\n\n if (input.runtime === 'lumen') {\n config.build = LUMEN_BUILD.command;\n config.outDir = LUMEN_BUILD.outDir;\n } else {\n const spec = RUNTIMES[input.runtime];\n config.runtimeVersion = spec.defaultVersion;\n config.preset = spec.presets[0]?.[0];\n config.start = input.start ?? spec.command;\n config.public = true;\n config.build = '';\n }\n return config;\n}\n","import { access } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n type AccountApp,\n RUNTIMES,\n type Roxyon,\n RoxyonApiError,\n isAuthError,\n} from '@roxyon/api-client';\nimport { packDirectory } from './archive.js';\nimport { type ProjectConfig, loadProjectConfig, saveProjectConfig } from './project.js';\nimport { runShellCommand } from './run-command.js';\n\nexport interface DeployReporter {\n /** A high-level phase change (\"Building\", \"Uploading\", \"Live\"). */\n step?: (msg: string) => void;\n /** A single build- or runtime-log line. */\n log?: (line: string) => void;\n}\n\nexport interface DeployParams {\n cwd: string;\n /** An authenticated client. */\n roxyon: Roxyon;\n /** Subscription objectId / Name to prefer when the account has several. */\n preferredSubscription?: string;\n /** Run the configured build command (default true). */\n build?: boolean;\n /** For app deploys: poll until running/failed (default true). */\n follow?: boolean;\n pollMs?: number;\n timeoutMs?: number;\n reporter?: DeployReporter;\n /** Override the build-command runner (default: spawn a shell). */\n runCommand?: (command: string, cwd: string, reporter?: DeployReporter) => Promise<number>;\n}\n\nexport type DeployOutcome =\n | { kind: 'static'; ok: true; url: string; files: number; config: ProjectConfig }\n | {\n kind: 'app';\n ok: true;\n application: string;\n status: string;\n revision: number;\n url?: string;\n config: ProjectConfig;\n logs: string[];\n }\n | {\n kind: 'app';\n ok: false;\n application: string;\n status: string;\n error: string;\n config: ProjectConfig;\n logs: string[];\n };\n\nexport class DeployError extends Error {\n readonly code:\n | 'no-config'\n | 'auth'\n | 'bad-host'\n | 'build-failed'\n | 'no-build-output'\n | 'upload-failed'\n | 'create-failed';\n constructor(message: string, code: DeployError['code']) {\n super(message);\n this.name = 'DeployError';\n this.code = code;\n }\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Build (if configured), archive, upload, and — for app runtimes — poll until\n * the deploy settles. Self-contained: one `/account/context` call validates the\n * host (works with a session token or a `roxp_` PAT), and the console creates\n * the application on the first deploy — the new id is written back to\n * `roxyon.json`.\n */\nexport async function deployProject(params: DeployParams): Promise<DeployOutcome> {\n const { cwd, roxyon, reporter } = params;\n const runCommand = params.runCommand ?? runShellCommand;\n\n const config = await loadProjectConfig(cwd);\n if (!config) {\n throw new DeployError('No roxyon.json in this directory — run init first.', 'no-config');\n }\n\n // One call — works with a session token or a PAT.\n const ctx = await roxyon.account.context().catch((e) => {\n if (isAuthError(e)) throw new DeployError('Session expired — sign in again.', 'auth');\n throw e;\n });\n const domain = ctx.domains.find((d) => d.name === config.host);\n if (!domain) {\n throw new DeployError(\n `Host \"${config.host}\" is not on this account (have: ${\n ctx.domains.map((d) => d.name).join(', ') || 'none'\n }).`,\n 'bad-host',\n );\n }\n\n // ---- build ----\n if (config.build && params.build !== false) {\n reporter?.step?.(`Build: ${config.build}`);\n const code = await runCommand(config.build, cwd, reporter);\n if (code !== 0) throw new DeployError(`Build command exited ${code}.`, 'build-failed');\n }\n\n // ---- pack ----\n const artifactDir = config.kind === 'static' && config.outDir ? resolve(cwd, config.outDir) : cwd;\n if (config.kind === 'static' && config.outDir) {\n await access(artifactDir).catch(() => {\n throw new DeployError(\n `Build output \"${config.outDir}\" not found — did the build run?`,\n 'no-build-output',\n );\n });\n }\n reporter?.step?.(`Packing ${artifactDir === cwd ? '.' : config.outDir}`);\n const pack = await packDirectory(artifactDir);\n reporter?.log?.(`${pack.files.length} files · ${(pack.bytes / 1024).toFixed(0)} KB`);\n\n // ---- static ----\n if (config.kind === 'static') {\n reporter?.step?.(`Uploading to ${config.host}${config.folder ? `/${config.folder}` : ''}`);\n let res: Awaited<ReturnType<typeof roxyon.sites.deploy>>;\n try {\n res = await roxyon.sites.deploy(config.host, config.folder, pack.buffer);\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n 'upload-failed',\n );\n }\n const url = `https://${res.host}${res.path && res.path !== '/' ? res.path : ''}`;\n reporter?.step?.(`Live — ${url}`);\n return { kind: 'static', ok: true, url, files: res.files ?? pack.files.length, config };\n }\n\n // ---- app ----\n // The console's /applications/deploy creates the app rows on the first deploy\n // (no `application` id yet) from the host + runtime params — one trusted call,\n // so a PAT-only CI job needs no BaaS write access.\n const runtime = config.runtime === 'lumen' ? 'node' : config.runtime;\n const spec = RUNTIMES[runtime];\n const target = config.application ?? {\n host: config.host,\n folder: config.folder,\n runtime,\n runtimeVersion: config.runtimeVersion ?? spec.defaultVersion,\n preset: config.preset ?? spec.presets[0]?.[0] ?? runtime,\n command: config.start ?? spec.command,\n public: config.public ?? true,\n };\n\n reporter?.step?.(\n config.application ? 'Uploading source' : 'Creating the application & uploading',\n );\n let revision = 0;\n let appId: string;\n try {\n const res = await roxyon.applications.uploadSource(target, pack.buffer);\n revision = res.configRevision ?? 0;\n appId = res.application;\n if (res.created && appId && appId !== config.application) {\n config.application = appId;\n await saveProjectConfig(config, cwd);\n reporter?.log?.(`application ${appId} created`);\n }\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n config.application ? 'upload-failed' : 'create-failed',\n );\n }\n\n const url = config.public\n ? `https://${config.host}${config.folder ? `/${config.folder}` : ''}`\n : undefined;\n\n if (params.follow === false) {\n reporter?.step?.(`Build queued (revision ${revision})`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'building',\n revision,\n url,\n config,\n logs: [],\n };\n }\n\n return watch(roxyon, appId, {\n reporter,\n url,\n config,\n pollMs: params.pollMs ?? 3000,\n timeoutMs: params.timeoutMs ?? 10 * 60 * 1000,\n });\n}\n\nasync function watch(\n roxyon: Roxyon,\n appId: string,\n opts: {\n reporter?: DeployReporter;\n url?: string;\n config: ProjectConfig;\n pollMs: number;\n timeoutMs: number;\n },\n): Promise<DeployOutcome> {\n const started = Date.now();\n const seen = new Set<string>();\n const collected: string[] = [];\n let lastStatus = '';\n\n opts.reporter?.step?.('Building');\n while (true) {\n await sleep(opts.pollMs);\n\n let app: AccountApp | undefined;\n try {\n app = await roxyon.account.getApp(appId);\n } catch {\n /* transient */\n }\n if (!app) {\n if (Date.now() - started > opts.timeoutMs) break;\n continue;\n }\n\n const cfg = app.configRevision;\n const applied = app.appliedRevision;\n const status = app.status;\n if (status !== lastStatus) {\n opts.reporter?.step?.(`${status || 'pending'} (${applied}/${cfg})`);\n lastStatus = status;\n }\n\n try {\n for (const line of await roxyon.applications.logs(appId, 40)) {\n if (line && !seen.has(line)) {\n seen.add(line);\n collected.push(line);\n opts.reporter?.log?.(line);\n }\n }\n } catch {\n /* transient */\n }\n\n if (cfg <= applied && status === 'running') {\n opts.reporter?.step?.(`Live${opts.url ? ` — ${opts.url}` : ''}`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'running',\n revision: applied,\n url: opts.url,\n config: opts.config,\n logs: collected,\n };\n }\n if (status === 'failed') {\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'failed',\n error: (app.lastError || 'Deploy failed.').trim(),\n config: opts.config,\n logs: collected,\n };\n }\n if (Date.now() - started > opts.timeoutMs) break;\n }\n\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'timeout',\n error: 'Still building after the timeout — check `roxyon logs`.',\n config: opts.config,\n logs: collected,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { DeployReporter } from './deploy.js';\n\n/**\n * Default build-command runner: spawn a shell, stream output to the reporter.\n * Callers in a non-local context (a hosted server) can pass their own.\n */\nexport function runShellCommand(\n command: string,\n cwd: string,\n reporter?: DeployReporter,\n): Promise<number> {\n return new Promise((resolvePromise) => {\n const child = spawn(command, { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n const pipe = (buf: Buffer) => {\n for (const line of buf.toString('utf8').split('\\n')) {\n if (line.trim()) reporter?.log?.(line);\n }\n };\n child.stdout?.on('data', pipe);\n child.stderr?.on('data', pipe);\n child.on('close', (code) => resolvePromise(code ?? 0));\n child.on('error', () => resolvePromise(127));\n });\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written\n * by `roxyon login`; read by the CLI and the MCP server alike.\n */\nexport interface Credentials {\n sessionToken: string;\n refreshToken?: string;\n userId: string;\n email?: string;\n /** Preferred subscription (objectId / Name) when the account has several. */\n subscription?: string;\n savedAt: string;\n}\n\nexport interface Endpoints {\n apiUrl?: string;\n consoleUrl?: string;\n}\n\nexport function credentialsDir(): string {\n return join(homedir(), '.roxyon');\n}\nexport function credentialsPath(): string {\n return join(credentialsDir(), 'config.json');\n}\n\n/** Endpoint overrides from the environment (`ROXYON_API_URL`, `ROXYON_CONSOLE_URL`). */\nexport function endpointsFromEnv(): Endpoints {\n return {\n apiUrl: process.env.ROXYON_API_URL,\n consoleUrl: process.env.ROXYON_CONSOLE_URL,\n };\n}\n\n/** A token supplied out-of-band (CI, or an MCP host env). Skips the stored login. */\nexport function tokenFromEnv(): string | undefined {\n return process.env.ROXYON_TOKEN || undefined;\n}\n\nexport async function loadCredentials(): Promise<Credentials | undefined> {\n try {\n return JSON.parse(await readFile(credentialsPath(), 'utf8')) as Credentials;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveCredentials(creds: Credentials): Promise<string> {\n const path = credentialsPath();\n await mkdir(credentialsDir(), { recursive: true, mode: 0o700 });\n await writeFile(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600).catch(() => undefined);\n return path;\n}\n\nexport async function clearCredentials(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n"],"mappings":";AAAA,SAAS,UAAU,SAAS,YAAY;AACxC,SAAS,MAAM,UAAU,WAAW;AACpC,OAAO,YAA6B;AACpC,SAAS,UAAU,iBAAiB;AAG7B,IAAM,gBAAgB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,eAAe,eAAe,MAAc,MAAiC;AAC3E,MAAI;AACF,YAAQ,MAAM,SAAS,KAAK,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,OAAO;AAAA,EACjE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAGA,eAAsB,YAAY,MAA+B;AAC/D,QAAM,KAAK,OAAO,EAAE,IAAI,aAAa;AACrC,QAAM,SAAS,MAAM,eAAe,MAAM,eAAe;AACzD,MAAI,OAAO,OAAQ,IAAG,IAAI,MAAM;AAAA,MAC3B,IAAG,IAAI,MAAM,eAAe,MAAM,YAAY,CAAC;AACpD,SAAO;AACT;AAGA,eAAsB,UAAU,MAAc,IAA+B;AAC3E,QAAM,QAAkB,CAAC;AAEzB,iBAAe,KAAK,KAA4B;AAC9C,UAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,eAAW,SAAS,SAAS;AAC3B,YAAM,MAAM,KAAK,KAAK,MAAM,IAAI;AAChC,YAAM,MAAM,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,KAAK,GAAG;AACnD,UAAI,CAAC,OAAO,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,GAAG,GAAG,EAAG;AACtD,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,KAAK,GAAG;AAAA,MAChB,WAAW,MAAM,OAAO,KAAK,MAAM,eAAe,GAAG;AACnD,cAAM,KAAK,GAAG;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,IAAI;AACf,SAAO,MAAM,KAAK;AACpB;AAYA,eAAsB,cACpB,MACA,OAA+C,CAAC,GAC3B;AACrB,QAAM,KAAK,MAAM,YAAY,IAAI;AACjC,QAAM,QAAQ,MAAM,UAAU,MAAM,EAAE;AACtC,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,IAAI;AAAA,MACR,0BAA0B,IAAI;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,SAAS;AAAA,IACb;AAAA,MACE,MAAM;AAAA,MACN,KAAK;AAAA,MACL,UAAU;AAAA,MACV,OAAO,oBAAI,KAAK,CAAC;AAAA;AAAA,MACjB,QAAQ,KAAK;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,IACA;AAAA,EACF;AAEA,QAAM,SAAmB,CAAC;AAC1B,MAAI,QAAQ;AACZ,QAAM,MAAM,KAAK,YAAY,MAAM,OAAO;AAC1C,mBAAiB,SAAS,QAAiC;AACzD,aAAS,MAAM;AACf,QAAI,QAAQ,KAAK;AACf,YAAM,IAAI;AAAA,QACR,0BAA2B,MAAM,OAAO,OAAQ,CAAC;AAAA,MACnD;AAAA,IACF;AACA,WAAO,KAAK,KAAK;AAAA,EACnB;AAEA,SAAO,EAAE,QAAQ,OAAO,OAAO,MAAM,GAAG,OAAO,MAAM;AACvD;AAGA,eAAsB,QAAQ,MAAc,OAAkC;AAC5E,MAAI,QAAQ;AACZ,aAAW,KAAK,OAAO;AACrB,QAAI;AACF,gBAAU,MAAM,KAAK,KAAK,MAAM,CAAC,CAAC,GAAG;AAAA,IACvC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;;;ACtHA,SAAS,YAAAA,WAAU,WAAAC,gBAAe;AAClC,SAAS,QAAAC,aAAY;AAWrB,eAAe,OAAO,MAAc,MAAgC;AAClE,MAAI;AACF,UAAMF,UAASE,MAAK,MAAM,IAAI,CAAC;AAC/B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,SAAS,MAAc,MAA4D;AAChG,MAAI;AACF,WAAO,KAAK,MAAM,MAAMF,UAASE,MAAK,MAAM,IAAI,GAAG,MAAM,CAAC;AAAA,EAC5D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAsB,cAAc,MAAkC;AACpE,QAAM,MAAM,MAAM,SAAS,MAAM,cAAc;AAC/C,QAAM,gBAAgB,MAAM,OAAO,MAAM,aAAa;AACtD,MAAI,WAAW;AACf,MAAI;AACF,gBAAY,MAAMD,SAAQC,MAAK,MAAM,OAAO,OAAO,CAAC,GAAG,UAAU;AAAA,EACnE,QAAQ;AACN,eAAW;AAAA,EACb;AAEA,MAAI,iBAAiB,UAAU;AAC7B,UAAM,OAAO;AAAA,MACX,GAAI,KAAK;AAAA,MACT,GAAI,KAAK;AAAA,IACX;AACA,QAAI,EAAE,UAAU,SAAS,EAAE,UAAU,SAAS,EAAE,aAAa,SAAS,EAAE,aAAa,OAAO;AAC1F,aAAO,EAAE,SAAS,SAAS,QAAQ,mDAAmD;AAAA,IACxF;AAAA,EACF;AAEA,MAAI,KAAK;AACP,UAAM,UAAW,IAAI,WAAsC,CAAC;AAC5D,UAAM,QAAQ,QAAQ,QAAQ,kBAAkB,QAAQ,QAAQ,kBAAkB;AAClF,UAAM,OAAO,EAAE,GAAI,IAAI,cAAyB,GAAI,IAAI,gBAA2B;AAInF,QAAI,UAAU;AACZ,aAAO,EAAE,SAAS,QAAQ,QAAQ,2BAA2B,OAAO,gBAAgB;AACtF,QAAI,UAAU;AACZ,aAAO,EAAE,SAAS,QAAQ,QAAQ,wBAAwB,OAAO,gBAAgB;AACnF,WAAO,EAAE,SAAS,QAAQ,QAAQ,wBAAwB,MAAM;AAAA,EAClE;AAEA,MAAK,MAAM,OAAO,MAAM,kBAAkB,KAAO,MAAM,OAAO,MAAM,gBAAgB,GAAI;AACtF,WAAO;AAAA,MACL,SAAS;AAAA,MACT,QAAQ;AAAA,MACR,OAAO;AAAA,IACT;AAAA,EACF;AAEA,MAAK,MAAM,OAAO,MAAM,eAAe,KAAO,MAAM,OAAO,MAAM,YAAY,GAAI;AAC/E,WAAO,EAAE,SAAS,OAAO,QAAQ,8BAA8B,OAAO,oBAAoB;AAAA,EAC5F;AAEA,SAAO,EAAE,SAAS,SAAS,QAAQ,wDAAmD;AACxF;;;AC7EA,SAAS,OAAO,YAAAC,WAAU,iBAAiB;AAC3C,SAAS,UAAU,SAAS,eAAe;AAC3C,SAA0B,aAAa,gBAAkC;AA0BlE,IAAM,eAAe;AAErB,SAAS,kBAAkB,MAAc,QAAQ,IAAI,GAAW;AACrE,SAAO,QAAQ,KAAK,YAAY;AAClC;AAEA,eAAsB,kBACpB,MAAc,QAAQ,IAAI,GACU;AACpC,MAAI;AACF,WAAO,KAAK,MAAM,MAAMA,UAAS,kBAAkB,GAAG,GAAG,MAAM,CAAC;AAAA,EAClE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,kBACpB,QACA,MAAc,QAAQ,IAAI,GACT;AACjB,QAAM,OAAO,kBAAkB,GAAG;AAClC,QAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,QAAM,UAAU,MAAM,GAAG,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,CAAI;AAC5D,SAAO;AACT;AAeO,SAAS,mBAAmB,OAAwC;AACzE,QAAM,SAAS,OAAO,MAAM,UAAU,EAAE,EAAE,QAAQ,cAAc,EAAE;AAClE,QAAM,SAAwB;AAAA,IAC5B,MAAM,MAAM,QAAQ,SAAS,MAAM,GAAG;AAAA,IACtC,MAAM,MAAM;AAAA,IACZ;AAAA,IACA,SAAS,MAAM;AAAA,IACf,MAAM,MAAM,YAAY,UAAU,WAAW;AAAA,EAC/C;AAEA,MAAI,MAAM,YAAY,SAAS;AAC7B,WAAO,QAAQ,YAAY;AAC3B,WAAO,SAAS,YAAY;AAAA,EAC9B,OAAO;AACL,UAAM,OAAO,SAAS,MAAM,OAAO;AACnC,WAAO,iBAAiB,KAAK;AAC7B,WAAO,SAAS,KAAK,QAAQ,CAAC,IAAI,CAAC;AACnC,WAAO,QAAQ,MAAM,SAAS,KAAK;AACnC,WAAO,SAAS;AAChB,WAAO,QAAQ;AAAA,EACjB;AACA,SAAO;AACT;;;ACzFA,SAAS,cAAc;AACvB,SAAS,WAAAC,gBAAe;AACxB;AAAA,EAEE,YAAAC;AAAA,EAEA;AAAA,EACA;AAAA,OACK;;;ACRP,SAAS,aAAa;AAOf,SAAS,gBACd,SACA,KACA,UACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,mBAAmB;AACrC,UAAM,QAAQ,MAAM,SAAS,EAAE,KAAK,OAAO,MAAM,OAAO,CAAC,UAAU,QAAQ,MAAM,EAAE,CAAC;AACpF,UAAM,OAAO,CAAC,QAAgB;AAC5B,iBAAW,QAAQ,IAAI,SAAS,MAAM,EAAE,MAAM,IAAI,GAAG;AACnD,YAAI,KAAK,KAAK,EAAG,WAAU,MAAM,IAAI;AAAA,MACvC;AAAA,IACF;AACA,UAAM,QAAQ,GAAG,QAAQ,IAAI;AAC7B,UAAM,QAAQ,GAAG,QAAQ,IAAI;AAC7B,UAAM,GAAG,SAAS,CAAC,SAAS,eAAe,QAAQ,CAAC,CAAC;AACrD,UAAM,GAAG,SAAS,MAAM,eAAe,GAAG,CAAC;AAAA,EAC7C,CAAC;AACH;;;ADmCO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAC5B;AAAA,EAQT,YAAY,SAAiB,MAA2B;AACtD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEA,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AASlE,eAAsB,cAAc,QAA8C;AAChF,QAAM,EAAE,KAAK,QAAQ,SAAS,IAAI;AAClC,QAAM,aAAa,OAAO,cAAc;AAExC,QAAM,SAAS,MAAM,kBAAkB,GAAG;AAC1C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,YAAY,2DAAsD,WAAW;AAAA,EACzF;AAGA,QAAM,MAAM,MAAM,OAAO,QAAQ,QAAQ,EAAE,MAAM,CAAC,MAAM;AACtD,QAAI,YAAY,CAAC,EAAG,OAAM,IAAI,YAAY,yCAAoC,MAAM;AACpF,UAAM;AAAA,EACR,CAAC;AACD,QAAM,SAAS,IAAI,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,IAAI;AAC7D,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI;AAAA,MACR,SAAS,OAAO,IAAI,mCAClB,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,KAAK,MAC/C;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAGA,MAAI,OAAO,SAAS,OAAO,UAAU,OAAO;AAC1C,cAAU,OAAO,UAAU,OAAO,KAAK,EAAE;AACzC,UAAM,OAAO,MAAM,WAAW,OAAO,OAAO,KAAK,QAAQ;AACzD,QAAI,SAAS,EAAG,OAAM,IAAI,YAAY,wBAAwB,IAAI,KAAK,cAAc;AAAA,EACvF;AAGA,QAAM,cAAc,OAAO,SAAS,YAAY,OAAO,SAASC,SAAQ,KAAK,OAAO,MAAM,IAAI;AAC9F,MAAI,OAAO,SAAS,YAAY,OAAO,QAAQ;AAC7C,UAAM,OAAO,WAAW,EAAE,MAAM,MAAM;AACpC,YAAM,IAAI;AAAA,QACR,iBAAiB,OAAO,MAAM;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACA,YAAU,OAAO,WAAW,gBAAgB,MAAM,MAAM,OAAO,MAAM,EAAE;AACvE,QAAM,OAAO,MAAM,cAAc,WAAW;AAC5C,YAAU,MAAM,GAAG,KAAK,MAAM,MAAM,gBAAa,KAAK,QAAQ,MAAM,QAAQ,CAAC,CAAC,KAAK;AAGnF,MAAI,OAAO,SAAS,UAAU;AAC5B,cAAU,OAAO,gBAAgB,OAAO,IAAI,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,KAAK,EAAE,EAAE;AACzF,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,OAAO,MAAM,OAAO,OAAO,MAAM,OAAO,QAAQ,KAAK,MAAM;AAAA,IACzE,SAAS,KAAK;AACZ,YAAM,IAAI;AAAA,QACR,eAAe,iBAAiB,IAAI,UAAU,OAAO,GAAG;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AACA,UAAMC,OAAM,WAAW,IAAI,IAAI,GAAG,IAAI,QAAQ,IAAI,SAAS,MAAM,IAAI,OAAO,EAAE;AAC9E,cAAU,OAAO,eAAUA,IAAG,EAAE;AAChC,WAAO,EAAE,MAAM,UAAU,IAAI,MAAM,KAAAA,MAAK,OAAO,IAAI,SAAS,KAAK,MAAM,QAAQ,OAAO;AAAA,EACxF;AAMA,QAAM,UAAU,OAAO,YAAY,UAAU,SAAS,OAAO;AAC7D,QAAM,OAAOC,UAAS,OAAO;AAC7B,QAAM,SAAS,OAAO,eAAe;AAAA,IACnC,MAAM,OAAO;AAAA,IACb,QAAQ,OAAO;AAAA,IACf;AAAA,IACA,gBAAgB,OAAO,kBAAkB,KAAK;AAAA,IAC9C,QAAQ,OAAO,UAAU,KAAK,QAAQ,CAAC,IAAI,CAAC,KAAK;AAAA,IACjD,SAAS,OAAO,SAAS,KAAK;AAAA,IAC9B,QAAQ,OAAO,UAAU;AAAA,EAC3B;AAEA,YAAU;AAAA,IACR,OAAO,cAAc,qBAAqB;AAAA,EAC5C;AACA,MAAI,WAAW;AACf,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,aAAa,aAAa,QAAQ,KAAK,MAAM;AACtE,eAAW,IAAI,kBAAkB;AACjC,YAAQ,IAAI;AACZ,QAAI,IAAI,WAAW,SAAS,UAAU,OAAO,aAAa;AACxD,aAAO,cAAc;AACrB,YAAM,kBAAkB,QAAQ,GAAG;AACnC,gBAAU,MAAM,eAAe,KAAK,UAAU;AAAA,IAChD;AAAA,EACF,SAAS,KAAK;AACZ,UAAM,IAAI;AAAA,MACR,eAAe,iBAAiB,IAAI,UAAU,OAAO,GAAG;AAAA,MACxD,OAAO,cAAc,kBAAkB;AAAA,IACzC;AAAA,EACF;AAEA,QAAM,MAAM,OAAO,SACf,WAAW,OAAO,IAAI,GAAG,OAAO,SAAS,IAAI,OAAO,MAAM,KAAK,EAAE,KACjE;AAEJ,MAAI,OAAO,WAAW,OAAO;AAC3B,cAAU,OAAO,0BAA0B,QAAQ,GAAG;AACtD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,aAAa;AAAA,MACb,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,MAAM,CAAC;AAAA,IACT;AAAA,EACF;AAEA,SAAO,MAAM,QAAQ,OAAO;AAAA,IAC1B;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,OAAO,UAAU;AAAA,IACzB,WAAW,OAAO,aAAa,KAAK,KAAK;AAAA,EAC3C,CAAC;AACH;AAEA,eAAe,MACb,QACA,OACA,MAOwB;AACxB,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,YAAsB,CAAC;AAC7B,MAAI,aAAa;AAEjB,OAAK,UAAU,OAAO,UAAU;AAChC,SAAO,MAAM;AACX,UAAM,MAAM,KAAK,MAAM;AAEvB,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,OAAO,QAAQ,OAAO,KAAK;AAAA,IACzC,QAAQ;AAAA,IAER;AACA,QAAI,CAAC,KAAK;AACR,UAAI,KAAK,IAAI,IAAI,UAAU,KAAK,UAAW;AAC3C;AAAA,IACF;AAEA,UAAM,MAAM,IAAI;AAChB,UAAM,UAAU,IAAI;AACpB,UAAM,SAAS,IAAI;AACnB,QAAI,WAAW,YAAY;AACzB,WAAK,UAAU,OAAO,GAAG,UAAU,SAAS,KAAK,OAAO,IAAI,GAAG,GAAG;AAClE,mBAAa;AAAA,IACf;AAEA,QAAI;AACF,iBAAW,QAAQ,MAAM,OAAO,aAAa,KAAK,OAAO,EAAE,GAAG;AAC5D,YAAI,QAAQ,CAAC,KAAK,IAAI,IAAI,GAAG;AAC3B,eAAK,IAAI,IAAI;AACb,oBAAU,KAAK,IAAI;AACnB,eAAK,UAAU,MAAM,IAAI;AAAA,QAC3B;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,QAAI,OAAO,WAAW,WAAW,WAAW;AAC1C,WAAK,UAAU,OAAO,OAAO,KAAK,MAAM,WAAM,KAAK,GAAG,KAAK,EAAE,EAAE;AAC/D,aAAO;AAAA,QACL,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,KAAK,KAAK;AAAA,QACV,QAAQ,KAAK;AAAA,QACb,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,WAAW,UAAU;AACvB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,QAAQ;AAAA,QACR,QAAQ,IAAI,aAAa,kBAAkB,KAAK;AAAA,QAChD,QAAQ,KAAK;AAAA,QACb,MAAM;AAAA,MACR;AAAA,IACF;AACA,QAAI,KAAK,IAAI,IAAI,UAAU,KAAK,UAAW;AAAA,EAC7C;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI;AAAA,IACJ,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ,KAAK;AAAA,IACb,MAAM;AAAA,EACR;AACF;;;AEzSA,SAAS,OAAO,SAAAC,QAAO,YAAAC,WAAU,IAAI,aAAAC,kBAAiB;AACtD,SAAS,eAAe;AACxB,SAAS,QAAAC,aAAY;AAqBd,SAAS,iBAAyB;AACvC,SAAOA,MAAK,QAAQ,GAAG,SAAS;AAClC;AACO,SAAS,kBAA0B;AACxC,SAAOA,MAAK,eAAe,GAAG,aAAa;AAC7C;AAGO,SAAS,mBAA8B;AAC5C,SAAO;AAAA,IACL,QAAQ,QAAQ,IAAI;AAAA,IACpB,YAAY,QAAQ,IAAI;AAAA,EAC1B;AACF;AAGO,SAAS,eAAmC;AACjD,SAAO,QAAQ,IAAI,gBAAgB;AACrC;AAEA,eAAsB,kBAAoD;AACxE,MAAI;AACF,WAAO,KAAK,MAAM,MAAMF,UAAS,gBAAgB,GAAG,MAAM,CAAC;AAAA,EAC7D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,gBAAgB,OAAqC;AACzE,QAAM,OAAO,gBAAgB;AAC7B,QAAMD,OAAM,eAAe,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAC9D,QAAME,WAAU,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC5E,QAAM,MAAM,MAAM,GAAK,EAAE,MAAM,MAAM,MAAS;AAC9C,SAAO;AACT;AAEA,eAAsB,mBAAkC;AACtD,QAAM,GAAG,gBAAgB,GAAG,EAAE,OAAO,KAAK,CAAC;AAC7C;","names":["readFile","readdir","join","readFile","resolve","RUNTIMES","resolve","url","RUNTIMES","mkdir","readFile","writeFile","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/archive.ts","../src/detect.ts","../src/project.ts","../src/deploy.ts","../src/run-command.ts","../src/wordpress.ts","../src/credentials.ts"],"sourcesContent":["import { mkdir, mkdtemp, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { dirname, isAbsolute, join, relative, sep } from 'node:path';\nimport ignore, { type Ignore } from 'ignore';\nimport { create as tarCreate } from 'tar';\n\n/** Always excluded, regardless of ignore files — build artefacts and VCS/OS cruft. */\nexport const ALWAYS_IGNORE = [\n 'node_modules',\n '.git',\n '.next',\n '.nuxt',\n '.venv',\n '__pycache__',\n '.DS_Store',\n '.roxyon',\n 'dist/.cache',\n];\n\nasync function readIgnoreFile(root: string, name: string): Promise<string[]> {\n try {\n return (await readFile(join(root, name), 'utf8')).split(/\\r?\\n/);\n } catch {\n return [];\n }\n}\n\n/** Build the ignore matcher: `.roxyonignore` if present, else `.gitignore`, plus the always-list. */\nexport async function buildIgnore(root: string): Promise<Ignore> {\n const ig = ignore().add(ALWAYS_IGNORE);\n const roxyon = await readIgnoreFile(root, '.roxyonignore');\n if (roxyon.length) ig.add(roxyon);\n else ig.add(await readIgnoreFile(root, '.gitignore'));\n return ig;\n}\n\n/** Recursively list files under `root` that survive the ignore matcher (POSIX-relative paths). */\nexport async function listFiles(root: string, ig: Ignore): Promise<string[]> {\n const files: string[] = [];\n\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n const abs = join(dir, entry.name);\n const rel = relative(root, abs).split(sep).join('/');\n if (!rel || ig.ignores(rel) || ig.ignores(`${rel}/`)) continue;\n if (entry.isDirectory()) {\n await walk(abs);\n } else if (entry.isFile() || entry.isSymbolicLink()) {\n files.push(rel);\n }\n }\n }\n\n await walk(root);\n return files.sort();\n}\n\nexport interface PackResult {\n buffer: Buffer;\n files: string[];\n bytes: number;\n}\n\n/**\n * Create a deterministic gzipped tarball of `root` (minus ignored paths).\n * `prefix`, when given, is prepended to every entry path inside the archive.\n */\nexport async function packDirectory(\n root: string,\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n const ig = await buildIgnore(root);\n const files = await listFiles(root, ig);\n if (files.length === 0) {\n throw new Error(\n `Nothing to deploy from ${root} — every file is ignored or the folder is empty.`,\n );\n }\n\n const stream = tarCreate(\n {\n gzip: true,\n cwd: root,\n portable: true,\n mtime: new Date(0), // stable -> byte-identical archive for an unchanged tree\n prefix: opts.prefix,\n follow: false,\n },\n files,\n );\n\n const chunks: Buffer[] = [];\n let bytes = 0;\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n for await (const chunk of stream as AsyncIterable<Buffer>) {\n bytes += chunk.length;\n if (bytes > max) {\n throw new Error(\n `Deploy archive exceeds ${(max / 1024 / 1024) | 0} MB. Add large files to .roxyonignore.`,\n );\n }\n chunks.push(chunk);\n }\n\n return { buffer: Buffer.concat(chunks), files, bytes };\n}\n\nexport interface FileEntry {\n /** POSIX-style relative path inside the site, e.g. `index.html`, `about/index.html`. */\n path: string;\n /** File body — UTF-8 text, or base64 when `encoding: 'base64'`. */\n content: string;\n encoding?: 'utf8' | 'base64';\n}\n\n/** A path is safe if it is relative, has no `..`/`.` segment, and no NUL. */\nfunction safeEntryPath(p: string): string | null {\n const norm = p.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n if (!norm || norm.startsWith('/') || isAbsolute(norm) || norm.includes('\\0')) return null;\n if (norm.split('/').some((seg) => seg === '..' || seg === '.' || seg === '')) return null;\n return norm;\n}\n\n/**\n * Pack an in-memory list of `{ path, content }` into the same deterministic\n * gzip tarball {@link packDirectory} produces — materialise to a temp dir, then\n * reuse that path so the archive format and determinism are identical. Rejects\n * absolute paths and `..` traversal; `maxBytes` caps the total *decoded* content.\n */\nexport async function packFiles(\n entries: FileEntry[],\n opts: { prefix?: string; maxBytes?: number } = {},\n): Promise<PackResult> {\n if (entries.length === 0) throw new Error('No files to deploy.');\n\n const seen = new Set<string>();\n const clean: { path: string; buf: Buffer }[] = [];\n let total = 0;\n for (const e of entries) {\n const rel = safeEntryPath(e.path);\n if (!rel) throw new Error(`Unsafe file path: ${JSON.stringify(e.path)}`);\n if (seen.has(rel)) throw new Error(`Duplicate file path: ${rel}`);\n seen.add(rel);\n const buf = Buffer.from(e.content, e.encoding === 'base64' ? 'base64' : 'utf8');\n total += buf.length;\n clean.push({ path: rel, buf });\n }\n const max = opts.maxBytes ?? 512 * 1024 * 1024;\n if (total > max) {\n throw new Error(\n `Content is ${(total / 1024 / 1024).toFixed(1)} MB, which exceeds the ${(max / 1024 / 1024).toFixed(1)} MB limit.`,\n );\n }\n\n const dir = await mkdtemp(join(tmpdir(), 'roxyon-content-'));\n try {\n for (const { path: rel, buf } of clean) {\n const abs = join(dir, rel);\n await mkdir(dirname(abs), { recursive: true });\n await writeFile(abs, buf);\n }\n return await packDirectory(dir, opts);\n } finally {\n await rm(dir, { recursive: true, force: true });\n }\n}\n\n/** Total on-disk size of a set of files. */\nexport async function measure(root: string, files: string[]): Promise<number> {\n let total = 0;\n for (const f of files) {\n try {\n total += (await stat(join(root, f))).size;\n } catch {\n /* ignore */\n }\n }\n return total;\n}\n","import { readFile, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { RuntimeName } from '@roxyon/api-client';\n\nexport interface Detection {\n runtime: RuntimeName;\n /** why we think so — shown to the user, who can override */\n reason: string;\n /** best-guess start command for a server runtime */\n start?: string;\n}\n\nasync function exists(root: string, name: string): Promise<boolean> {\n try {\n await readFile(join(root, name));\n return true;\n } catch {\n return false;\n }\n}\n\nasync function readJson(root: string, name: string): Promise<Record<string, unknown> | undefined> {\n try {\n return JSON.parse(await readFile(join(root, name), 'utf8')) as Record<string, unknown>;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort project-type detection. Deterministic; the caller confirms. */\nexport async function detectRuntime(root: string): Promise<Detection> {\n const pkg = await readJson(root, 'package.json');\n const hasConfigJson = await exists(root, 'config.json');\n let hasViews = false;\n try {\n hasViews = (await readdir(join(root, 'src', 'views'))).length >= 0;\n } catch {\n hasViews = false;\n }\n\n if (hasConfigJson && hasViews) {\n const deps = {\n ...(pkg?.dependencies as object),\n ...(pkg?.devDependencies as object),\n } as Record<string, string>;\n if (!('next' in deps) && !('nuxt' in deps) && !('express' in deps) && !('fastify' in deps)) {\n return { runtime: 'lumen', reason: 'config.json + src/views/ and no server framework' };\n }\n }\n\n if (pkg) {\n const scripts = (pkg.scripts as Record<string, string>) ?? {};\n const start = scripts.start ? 'npm run start' : scripts.serve ? 'npm run serve' : 'node .';\n const deps = { ...(pkg.dependencies as object), ...(pkg.devDependencies as object) } as Record<\n string,\n string\n >;\n if ('next' in deps)\n return { runtime: 'node', reason: 'Next.js in package.json', start: 'npm run start' };\n if ('nuxt' in deps)\n return { runtime: 'node', reason: 'Nuxt in package.json', start: 'npm run start' };\n return { runtime: 'node', reason: 'package.json present', start };\n }\n\n if ((await exists(root, 'requirements.txt')) || (await exists(root, 'pyproject.toml'))) {\n return {\n runtime: 'python',\n reason: 'requirements.txt / pyproject.toml',\n start: 'gunicorn app:app',\n };\n }\n\n if ((await exists(root, 'composer.json')) || (await exists(root, 'server.php'))) {\n return { runtime: 'php', reason: 'composer.json / server.php', start: 'php8.4 server.php' };\n }\n\n return { runtime: 'lumen', reason: 'no framework markers — treating as a static site' };\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, dirname, resolve } from 'node:path';\nimport { type DeployKind, LUMEN_BUILD, RUNTIMES, type RuntimeName } from '@roxyon/api-client';\n\nexport type { DeployKind };\n\nexport interface ProjectConfig {\n name: string;\n /** `Applications.objectId`, filled after the first `app`-kind deploy. */\n application?: string;\n /** Route host — `Domains.Name`. */\n host: string;\n /** Sub-path under the host's docroot / source tree. `\"\"` = root. */\n folder: string;\n runtime: RuntimeName;\n runtimeVersion?: string;\n preset?: string;\n kind: DeployKind;\n /** Local build command (LumenJS / static). Empty for server runtimes. */\n build?: string;\n /** Directory the build writes to (relative to project root). */\n outDir?: string;\n /** Web-process start command (app kind only). */\n start?: string;\n /** Serve the app on the host instead of the host's own files. */\n public?: boolean;\n}\n\nexport const PROJECT_FILE = 'roxyon.json';\n\nexport function projectConfigPath(cwd: string = process.cwd()): string {\n return resolve(cwd, PROJECT_FILE);\n}\n\nexport async function loadProjectConfig(\n cwd: string = process.cwd(),\n): Promise<ProjectConfig | undefined> {\n try {\n return JSON.parse(await readFile(projectConfigPath(cwd), 'utf8')) as ProjectConfig;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveProjectConfig(\n config: ProjectConfig,\n cwd: string = process.cwd(),\n): Promise<string> {\n const path = projectConfigPath(cwd);\n await mkdir(dirname(path), { recursive: true });\n await writeFile(path, `${JSON.stringify(config, null, 2)}\\n`);\n return path;\n}\n\nexport interface BuildConfigInput {\n cwd: string;\n runtime: RuntimeName;\n host: string;\n folder?: string;\n name?: string;\n start?: string;\n}\n\n/**\n * Produce a `ProjectConfig` from a resolved runtime + host. Shared by the CLI's\n * `roxyon init` and the MCP `roxyon_init` tool so both write the same shape.\n */\nexport function buildProjectConfig(input: BuildConfigInput): ProjectConfig {\n const folder = String(input.folder ?? '').replace(/^\\/+|\\/+$/g, '');\n const config: ProjectConfig = {\n name: input.name ?? basename(input.cwd),\n host: input.host,\n folder,\n runtime: input.runtime,\n kind: input.runtime === 'lumen' ? 'static' : 'app',\n };\n\n if (input.runtime === 'lumen') {\n config.build = LUMEN_BUILD.command;\n config.outDir = LUMEN_BUILD.outDir;\n } else {\n const spec = RUNTIMES[input.runtime];\n config.runtimeVersion = spec.defaultVersion;\n config.preset = spec.presets[0]?.[0];\n config.start = input.start ?? spec.command;\n config.public = true;\n config.build = '';\n }\n return config;\n}\n","import { access } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport {\n type AccountApp,\n RUNTIMES,\n type Roxyon,\n RoxyonApiError,\n isAuthError,\n} from '@roxyon/api-client';\nimport { packDirectory } from './archive.js';\nimport { type ProjectConfig, loadProjectConfig, saveProjectConfig } from './project.js';\nimport { runShellCommand } from './run-command.js';\n\nexport interface DeployReporter {\n /** A high-level phase change (\"Building\", \"Uploading\", \"Live\"). */\n step?: (msg: string) => void;\n /** A single build- or runtime-log line. */\n log?: (line: string) => void;\n}\n\nexport interface DeployParams {\n cwd: string;\n /** An authenticated client. */\n roxyon: Roxyon;\n /** Subscription objectId / Name to prefer when the account has several. */\n preferredSubscription?: string;\n /** Run the configured build command (default true). */\n build?: boolean;\n /** For app deploys: poll until running/failed (default true). */\n follow?: boolean;\n pollMs?: number;\n timeoutMs?: number;\n reporter?: DeployReporter;\n /** Override the build-command runner (default: spawn a shell). */\n runCommand?: (command: string, cwd: string, reporter?: DeployReporter) => Promise<number>;\n}\n\nexport type DeployOutcome =\n | { kind: 'static'; ok: true; url: string; files: number; config: ProjectConfig }\n | {\n kind: 'app';\n ok: true;\n application: string;\n status: string;\n revision: number;\n url?: string;\n config: ProjectConfig;\n logs: string[];\n }\n | {\n kind: 'app';\n ok: false;\n application: string;\n status: string;\n error: string;\n config: ProjectConfig;\n logs: string[];\n };\n\nexport class DeployError extends Error {\n readonly code:\n | 'no-config'\n | 'auth'\n | 'bad-host'\n | 'build-failed'\n | 'no-build-output'\n | 'upload-failed'\n | 'create-failed';\n constructor(message: string, code: DeployError['code']) {\n super(message);\n this.name = 'DeployError';\n this.code = code;\n }\n}\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Build (if configured), archive, upload, and — for app runtimes — poll until\n * the deploy settles. Self-contained: one `/account/context` call validates the\n * host (works with a session token or a `roxp_` PAT), and the console creates\n * the application on the first deploy — the new id is written back to\n * `roxyon.json`.\n */\nexport async function deployProject(params: DeployParams): Promise<DeployOutcome> {\n const { cwd, roxyon, reporter } = params;\n const runCommand = params.runCommand ?? runShellCommand;\n\n const config = await loadProjectConfig(cwd);\n if (!config) {\n throw new DeployError('No roxyon.json in this directory — run init first.', 'no-config');\n }\n\n // One call — works with a session token or a PAT.\n const ctx = await roxyon.account.context().catch((e) => {\n if (isAuthError(e)) throw new DeployError('Session expired — sign in again.', 'auth');\n throw e;\n });\n const domain = ctx.domains.find((d) => d.name === config.host);\n if (!domain) {\n throw new DeployError(\n `Host \"${config.host}\" is not on this account (have: ${\n ctx.domains.map((d) => d.name).join(', ') || 'none'\n }).`,\n 'bad-host',\n );\n }\n\n // ---- build ----\n if (config.build && params.build !== false) {\n reporter?.step?.(`Build: ${config.build}`);\n const code = await runCommand(config.build, cwd, reporter);\n if (code !== 0) throw new DeployError(`Build command exited ${code}.`, 'build-failed');\n }\n\n // ---- pack ----\n const artifactDir = config.kind === 'static' && config.outDir ? resolve(cwd, config.outDir) : cwd;\n if (config.kind === 'static' && config.outDir) {\n await access(artifactDir).catch(() => {\n throw new DeployError(\n `Build output \"${config.outDir}\" not found — did the build run?`,\n 'no-build-output',\n );\n });\n }\n reporter?.step?.(`Packing ${artifactDir === cwd ? '.' : config.outDir}`);\n const pack = await packDirectory(artifactDir);\n reporter?.log?.(`${pack.files.length} files · ${(pack.bytes / 1024).toFixed(0)} KB`);\n\n // ---- static ----\n if (config.kind === 'static') {\n reporter?.step?.(`Uploading to ${config.host}${config.folder ? `/${config.folder}` : ''}`);\n let res: Awaited<ReturnType<typeof roxyon.sites.deploy>>;\n try {\n res = await roxyon.sites.deploy(config.host, config.folder, pack.buffer);\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n 'upload-failed',\n );\n }\n const url = `https://${res.host}${res.path && res.path !== '/' ? res.path : ''}`;\n reporter?.step?.(`Live — ${url}`);\n return { kind: 'static', ok: true, url, files: res.files ?? pack.files.length, config };\n }\n\n // ---- app ----\n // The console's /applications/deploy creates the app rows on the first deploy\n // (no `application` id yet) from the host + runtime params — one trusted call,\n // so a PAT-only CI job needs no BaaS write access.\n const runtime = config.runtime === 'lumen' ? 'node' : config.runtime;\n const spec = RUNTIMES[runtime];\n const target = config.application ?? {\n host: config.host,\n folder: config.folder,\n runtime,\n runtimeVersion: config.runtimeVersion ?? spec.defaultVersion,\n preset: config.preset ?? spec.presets[0]?.[0] ?? runtime,\n command: config.start ?? spec.command,\n public: config.public ?? true,\n };\n\n reporter?.step?.(\n config.application ? 'Uploading source' : 'Creating the application & uploading',\n );\n let revision = 0;\n let appId: string;\n try {\n const res = await roxyon.applications.uploadSource(target, pack.buffer);\n revision = res.configRevision ?? 0;\n appId = res.application;\n if (res.created && appId && appId !== config.application) {\n config.application = appId;\n await saveProjectConfig(config, cwd);\n reporter?.log?.(`application ${appId} created`);\n }\n } catch (err) {\n throw new DeployError(\n err instanceof RoxyonApiError ? err.message : String(err),\n config.application ? 'upload-failed' : 'create-failed',\n );\n }\n\n const url = config.public\n ? `https://${config.host}${config.folder ? `/${config.folder}` : ''}`\n : undefined;\n\n if (params.follow === false) {\n reporter?.step?.(`Build queued (revision ${revision})`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'building',\n revision,\n url,\n config,\n logs: [],\n };\n }\n\n return watch(roxyon, appId, {\n reporter,\n url,\n config,\n pollMs: params.pollMs ?? 3000,\n timeoutMs: params.timeoutMs ?? 10 * 60 * 1000,\n });\n}\n\nasync function watch(\n roxyon: Roxyon,\n appId: string,\n opts: {\n reporter?: DeployReporter;\n url?: string;\n config: ProjectConfig;\n pollMs: number;\n timeoutMs: number;\n },\n): Promise<DeployOutcome> {\n const started = Date.now();\n const seen = new Set<string>();\n const collected: string[] = [];\n let lastStatus = '';\n\n opts.reporter?.step?.('Building');\n while (true) {\n await sleep(opts.pollMs);\n\n let app: AccountApp | undefined;\n try {\n app = await roxyon.account.getApp(appId);\n } catch {\n /* transient */\n }\n if (!app) {\n if (Date.now() - started > opts.timeoutMs) break;\n continue;\n }\n\n const cfg = app.configRevision;\n const applied = app.appliedRevision;\n const status = app.status;\n if (status !== lastStatus) {\n opts.reporter?.step?.(`${status || 'pending'} (${applied}/${cfg})`);\n lastStatus = status;\n }\n\n try {\n for (const line of await roxyon.applications.logs(appId, 40)) {\n if (line && !seen.has(line)) {\n seen.add(line);\n collected.push(line);\n opts.reporter?.log?.(line);\n }\n }\n } catch {\n /* transient */\n }\n\n if (cfg <= applied && status === 'running') {\n opts.reporter?.step?.(`Live${opts.url ? ` — ${opts.url}` : ''}`);\n return {\n kind: 'app',\n ok: true,\n application: appId,\n status: 'running',\n revision: applied,\n url: opts.url,\n config: opts.config,\n logs: collected,\n };\n }\n if (status === 'failed') {\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'failed',\n error: (app.lastError || 'Deploy failed.').trim(),\n config: opts.config,\n logs: collected,\n };\n }\n if (Date.now() - started > opts.timeoutMs) break;\n }\n\n return {\n kind: 'app',\n ok: false,\n application: appId,\n status: 'timeout',\n error: 'Still building after the timeout — check `roxyon logs`.',\n config: opts.config,\n logs: collected,\n };\n}\n","import { spawn } from 'node:child_process';\nimport type { DeployReporter } from './deploy.js';\n\n/**\n * Default build-command runner: spawn a shell, stream output to the reporter.\n * Callers in a non-local context (a hosted server) can pass their own.\n */\nexport function runShellCommand(\n command: string,\n cwd: string,\n reporter?: DeployReporter,\n): Promise<number> {\n return new Promise((resolvePromise) => {\n const child = spawn(command, { cwd, shell: true, stdio: ['ignore', 'pipe', 'pipe'] });\n const pipe = (buf: Buffer) => {\n for (const line of buf.toString('utf8').split('\\n')) {\n if (line.trim()) reporter?.log?.(line);\n }\n };\n child.stdout?.on('data', pipe);\n child.stderr?.on('data', pipe);\n child.on('close', (code) => resolvePromise(code ?? 0));\n child.on('error', () => resolvePromise(127));\n });\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { Readable } from 'node:stream';\nimport { extract } from 'tar';\n\nconst WORDPRESS_TARBALL_URL = 'https://wordpress.org/latest.tar.gz';\nconst WORDPRESS_SALTS_URL = 'https://api.wordpress.org/secret-key/1.1/salt/';\nconst PLACEHOLDER = 'put your unique phrase here';\n\nexport interface WordPressDbCredentials {\n name: string;\n username: string;\n password: string;\n /** `host:port`, e.g. `10.0.0.2:6033` — WordPress's `DB_HOST` accepts this form directly. */\n host: string;\n}\n\nexport interface InstallWordPressOptions {\n /** Destination directory — created if missing. Must be empty or not yet exist. */\n dir: string;\n db: WordPressDbCredentials;\n /** Default `wp_`. */\n tablePrefix?: string;\n /** Override for tests — must resolve like the global `fetch`. */\n fetchImpl?: typeof fetch;\n}\n\nexport interface InstallWordPressResult {\n dir: string;\n}\n\n/**\n * Download WordPress core into `dir` and write a working `wp-config.php`\n * against the given database. Does not touch the network beyond\n * wordpress.org/api.wordpress.org, and does not deploy anywhere — pack `dir`\n * with {@link packDirectory} and upload with `roxyon.sites.deploy()` (the same\n * tar path `roxyon deploy` uses for static/app content; WordPress core is\n * thousands of files, well past the small JSON `roxyon_deploy_content` tool's\n * budget).\n */\nexport async function installWordPress(\n opts: InstallWordPressOptions,\n): Promise<InstallWordPressResult> {\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n await mkdir(opts.dir, { recursive: true });\n\n const coreRes = await fetchImpl(WORDPRESS_TARBALL_URL);\n if (!coreRes.ok) {\n throw new Error(`Could not download WordPress core (HTTP ${coreRes.status}).`);\n }\n const coreBuf = Buffer.from(await coreRes.arrayBuffer());\n\n // The official tarball has one top-level \"wordpress/\" directory — strip it\n // so core files land directly in `dir`.\n await new Promise<void>((resolvePromise, reject) => {\n const writer = extract({ cwd: opts.dir, strip: 1 });\n Readable.from(coreBuf)\n .pipe(writer)\n .on('finish', () => resolvePromise())\n .on('error', reject);\n });\n\n const saltsRes = await fetchImpl(WORDPRESS_SALTS_URL);\n const salts = saltsRes.ok ? (await saltsRes.text()).trim() : '';\n\n const samplePath = `${opts.dir}/wp-config-sample.php`;\n const sample = await readFile(samplePath, 'utf8');\n const config = buildWpConfig(sample, opts.db, opts.tablePrefix, salts);\n await writeFile(`${opts.dir}/wp-config.php`, config, 'utf8');\n\n return { dir: opts.dir };\n}\n\n/** Pure string transform — exported separately so it's testable without any network. */\nexport function buildWpConfig(\n sample: string,\n db: WordPressDbCredentials,\n tablePrefix = 'wp_',\n salts = '',\n): string {\n let out = sample\n .replace(/(['\"])database_name_here\\1/, `$1${escapePhp(db.name)}$1`)\n .replace(/(['\"])username_here\\1/, `$1${escapePhp(db.username)}$1`)\n .replace(/(['\"])password_here\\1/, `$1${escapePhp(db.password)}$1`)\n .replace(/(define\\(\\s*['\"]DB_HOST['\"]\\s*,\\s*)(['\"])localhost\\2/, `$1$2${escapePhp(db.host)}$2`)\n .replace(/(\\$table_prefix\\s*=\\s*)(['\"])wp_\\2/, `$1$2${escapePhp(tablePrefix)}$2`);\n\n if (salts) {\n const lines = out.split('\\n');\n const start = lines.findIndex((l) => l.includes(PLACEHOLDER));\n if (start !== -1) {\n let end = start;\n while (end < lines.length && lines[end]!.includes(PLACEHOLDER)) end++;\n lines.splice(start, end - start, salts);\n out = lines.join('\\n');\n }\n }\n\n return out;\n}\n\nfunction escapePhp(s: string): string {\n return s.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/**\n * Stored login for Roxyon tooling — `~/.roxyon/config.json`, mode 0600. Written\n * by `roxyon login`; read by the CLI and the MCP server alike.\n */\nexport interface Credentials {\n sessionToken: string;\n refreshToken?: string;\n userId: string;\n email?: string;\n /** Preferred subscription (objectId / Name) when the account has several. */\n subscription?: string;\n savedAt: string;\n}\n\nexport interface Endpoints {\n apiUrl?: string;\n consoleUrl?: string;\n}\n\nexport function credentialsDir(): string {\n return join(homedir(), '.roxyon');\n}\nexport function credentialsPath(): string {\n return join(credentialsDir(), 'config.json');\n}\n\n/** Endpoint overrides from the environment (`ROXYON_API_URL`, `ROXYON_CONSOLE_URL`). */\nexport function endpointsFromEnv(): Endpoints {\n return {\n apiUrl: process.env.ROXYON_API_URL,\n consoleUrl: process.env.ROXYON_CONSOLE_URL,\n };\n}\n\n/** A token supplied out-of-band (CI, or an MCP host env). Skips the stored login. */\nexport function tokenFromEnv(): string | undefined {\n return process.env.ROXYON_TOKEN || undefined;\n}\n\nexport async function loadCredentials(): Promise<Credentials | undefined> {\n try {\n return JSON.parse(await readFile(credentialsPath(), 'utf8')) as Credentials;\n } catch {\n return undefined;\n }\n}\n\nexport async function saveCredentials(creds: Credentials): Promise<string> {\n const path = credentialsPath();\n await mkdir(credentialsDir(), { recursive: true, mode: 0o700 });\n await writeFile(path, `${JSON.stringify(creds, null, 2)}\\n`, { mode: 0o600 });\n await chmod(path, 0o600).catch(() => undefined);\n return path;\n}\n\nexport async function clearCredentials(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n"],"mappings":"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","names":["readFile","readdir","join","mkdir","readFile","writeFile","dirname","resolve","RUNTIMES","resolve","url","RUNTIMES","mkdir","readFile","writeFile","mkdir","readFile","rm","writeFile","join"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roxyon/deploy-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Shared deploy pipeline for Roxyon tooling — project detection, archiving, and the build/upload/poll orchestration used by @roxyon/cli and @roxyon/mcp.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"ignore": "^5.3.2",
|
|
31
31
|
"tar": "^7.4.3",
|
|
32
|
-
"@roxyon/api-client": "0.
|
|
32
|
+
"@roxyon/api-client": "0.3.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^20.16.11",
|