@roxyon/deploy-core 0.1.1 → 0.2.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 +43 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +50 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -46,6 +46,7 @@ __export(index_exports, {
|
|
|
46
46
|
loadProjectConfig: () => loadProjectConfig,
|
|
47
47
|
measure: () => measure,
|
|
48
48
|
packDirectory: () => packDirectory,
|
|
49
|
+
packFiles: () => packFiles,
|
|
49
50
|
projectConfigPath: () => projectConfigPath,
|
|
50
51
|
runShellCommand: () => runShellCommand,
|
|
51
52
|
saveCredentials: () => saveCredentials,
|
|
@@ -56,6 +57,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
56
57
|
|
|
57
58
|
// src/archive.ts
|
|
58
59
|
var import_promises = require("fs/promises");
|
|
60
|
+
var import_node_os = require("os");
|
|
59
61
|
var import_node_path = require("path");
|
|
60
62
|
var import_ignore = __toESM(require("ignore"), 1);
|
|
61
63
|
var import_tar = require("tar");
|
|
@@ -136,6 +138,44 @@ async function packDirectory(root, opts = {}) {
|
|
|
136
138
|
}
|
|
137
139
|
return { buffer: Buffer.concat(chunks), files, bytes };
|
|
138
140
|
}
|
|
141
|
+
function safeEntryPath(p) {
|
|
142
|
+
const norm = p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
143
|
+
if (!norm || norm.startsWith("/") || (0, import_node_path.isAbsolute)(norm) || norm.includes("\0")) return null;
|
|
144
|
+
if (norm.split("/").some((seg) => seg === ".." || seg === "." || seg === "")) return null;
|
|
145
|
+
return norm;
|
|
146
|
+
}
|
|
147
|
+
async function packFiles(entries, opts = {}) {
|
|
148
|
+
if (entries.length === 0) throw new Error("No files to deploy.");
|
|
149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
150
|
+
const clean = [];
|
|
151
|
+
let total = 0;
|
|
152
|
+
for (const e of entries) {
|
|
153
|
+
const rel = safeEntryPath(e.path);
|
|
154
|
+
if (!rel) throw new Error(`Unsafe file path: ${JSON.stringify(e.path)}`);
|
|
155
|
+
if (seen.has(rel)) throw new Error(`Duplicate file path: ${rel}`);
|
|
156
|
+
seen.add(rel);
|
|
157
|
+
const buf = Buffer.from(e.content, e.encoding === "base64" ? "base64" : "utf8");
|
|
158
|
+
total += buf.length;
|
|
159
|
+
clean.push({ path: rel, buf });
|
|
160
|
+
}
|
|
161
|
+
const max = opts.maxBytes ?? 512 * 1024 * 1024;
|
|
162
|
+
if (total > max) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`Content is ${(total / 1024 / 1024).toFixed(1)} MB, which exceeds the ${(max / 1024 / 1024).toFixed(1)} MB limit.`
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
const dir = await (0, import_promises.mkdtemp)((0, import_node_path.join)((0, import_node_os.tmpdir)(), "roxyon-content-"));
|
|
168
|
+
try {
|
|
169
|
+
for (const { path: rel, buf } of clean) {
|
|
170
|
+
const abs = (0, import_node_path.join)(dir, rel);
|
|
171
|
+
await (0, import_promises.mkdir)((0, import_node_path.dirname)(abs), { recursive: true });
|
|
172
|
+
await (0, import_promises.writeFile)(abs, buf);
|
|
173
|
+
}
|
|
174
|
+
return await packDirectory(dir, opts);
|
|
175
|
+
} finally {
|
|
176
|
+
await (0, import_promises.rm)(dir, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
139
179
|
async function measure(root, files) {
|
|
140
180
|
let total = 0;
|
|
141
181
|
for (const f of files) {
|
|
@@ -459,10 +499,10 @@ async function watch(roxyon, appId, opts) {
|
|
|
459
499
|
|
|
460
500
|
// src/credentials.ts
|
|
461
501
|
var import_promises5 = require("fs/promises");
|
|
462
|
-
var
|
|
502
|
+
var import_node_os2 = require("os");
|
|
463
503
|
var import_node_path5 = require("path");
|
|
464
504
|
function credentialsDir() {
|
|
465
|
-
return (0, import_node_path5.join)((0,
|
|
505
|
+
return (0, import_node_path5.join)((0, import_node_os2.homedir)(), ".roxyon");
|
|
466
506
|
}
|
|
467
507
|
function credentialsPath() {
|
|
468
508
|
return (0, import_node_path5.join)(credentialsDir(), "config.json");
|
|
@@ -512,6 +552,7 @@ async function clearCredentials() {
|
|
|
512
552
|
loadProjectConfig,
|
|
513
553
|
measure,
|
|
514
554
|
packDirectory,
|
|
555
|
+
packFiles,
|
|
515
556
|
projectConfigPath,
|
|
516
557
|
runShellCommand,
|
|
517
558
|
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/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 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 { 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_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
|
|
|
@@ -165,4 +182,4 @@ declare function loadCredentials(): Promise<Credentials | undefined>;
|
|
|
165
182
|
declare function saveCredentials(creds: Credentials): Promise<string>;
|
|
166
183
|
declare function clearCredentials(): Promise<void>;
|
|
167
184
|
|
|
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 };
|
|
185
|
+
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, type FileEntry, PROJECT_FILE, type PackResult, type ProjectConfig, buildIgnore, buildProjectConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, 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
|
|
|
@@ -165,4 +182,4 @@ declare function loadCredentials(): Promise<Credentials | undefined>;
|
|
|
165
182
|
declare function saveCredentials(creds: Credentials): Promise<string>;
|
|
166
183
|
declare function clearCredentials(): Promise<void>;
|
|
167
184
|
|
|
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 };
|
|
185
|
+
export { ALWAYS_IGNORE, type BuildConfigInput, type Credentials, DeployError, type DeployOutcome, type DeployParams, type DeployReporter, type Detection, type Endpoints, type FileEntry, PROJECT_FILE, type PackResult, type ProjectConfig, buildIgnore, buildProjectConfig, clearCredentials, credentialsDir, credentialsPath, deployProject, detectRuntime, endpointsFromEnv, 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
|
}
|
|
@@ -406,7 +445,7 @@ async function watch(roxyon, appId, opts) {
|
|
|
406
445
|
}
|
|
407
446
|
|
|
408
447
|
// src/credentials.ts
|
|
409
|
-
import { chmod, mkdir as
|
|
448
|
+
import { chmod, mkdir as mkdir3, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
410
449
|
import { homedir } from "os";
|
|
411
450
|
import { join as join3 } from "path";
|
|
412
451
|
function credentialsDir() {
|
|
@@ -433,14 +472,14 @@ async function loadCredentials() {
|
|
|
433
472
|
}
|
|
434
473
|
async function saveCredentials(creds) {
|
|
435
474
|
const path = credentialsPath();
|
|
436
|
-
await
|
|
437
|
-
await
|
|
475
|
+
await mkdir3(credentialsDir(), { recursive: true, mode: 448 });
|
|
476
|
+
await writeFile3(path, `${JSON.stringify(creds, null, 2)}
|
|
438
477
|
`, { mode: 384 });
|
|
439
478
|
await chmod(path, 384).catch(() => void 0);
|
|
440
479
|
return path;
|
|
441
480
|
}
|
|
442
481
|
async function clearCredentials() {
|
|
443
|
-
await
|
|
482
|
+
await rm2(credentialsPath(), { force: true });
|
|
444
483
|
}
|
|
445
484
|
export {
|
|
446
485
|
ALWAYS_IGNORE,
|
|
@@ -459,6 +498,7 @@ export {
|
|
|
459
498
|
loadProjectConfig,
|
|
460
499
|
measure,
|
|
461
500
|
packDirectory,
|
|
501
|
+
packFiles,
|
|
462
502
|
projectConfigPath,
|
|
463
503
|
runShellCommand,
|
|
464
504
|
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":"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","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/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 { 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","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.2.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.2.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/node": "^20.16.11",
|