@omen.foundation/node-microservice-runtime 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -338,6 +338,18 @@ async function handlePublish(args, configStore, tokenManager) {
338
338
  const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host, false);
339
339
  const env = buildCommandEnv(profile, tokens);
340
340
  const scriptArgs = buildScriptArgs(forward, profile, true); // publish needs --api-host
341
+ // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided
342
+ const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));
343
+ if (!hasEnvFileFlag) {
344
+ const envPath = path.resolve('.env');
345
+ try {
346
+ await fs.access(envPath);
347
+ scriptArgs.push('--env-file', '.env');
348
+ }
349
+ catch {
350
+ // .env file doesn't exist, that's okay
351
+ }
352
+ }
341
353
  const scriptPath = path.resolve(__dirname, '../../scripts/publish-service.mjs');
342
354
  await runScript(scriptPath, scriptArgs, env);
343
355
  }
@@ -348,6 +360,18 @@ async function handleValidate(args, configStore, tokenManager) {
348
360
  const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host);
349
361
  const env = buildCommandEnv(profile, tokens);
350
362
  const scriptArgs = buildScriptArgs(forward, profile, false); // validate doesn't need --api-host
363
+ // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided
364
+ const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));
365
+ if (!hasEnvFileFlag) {
366
+ const envPath = path.resolve('.env');
367
+ try {
368
+ await fs.access(envPath);
369
+ scriptArgs.push('--env-file', '.env');
370
+ }
371
+ catch {
372
+ // .env file doesn't exist, that's okay
373
+ }
374
+ }
351
375
  const scriptPath = path.resolve(__dirname, '../../scripts/validate-service.mjs');
352
376
  await runScript(scriptPath, scriptArgs, env);
353
377
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\r\n\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport { spawn } from 'node:child_process';\r\nimport readline from 'node:readline';\r\nimport { stdin, stdout, exit } from 'node:process';\r\nimport { fileURLToPath } from 'node:url';\r\nimport dotenv from 'dotenv';\r\n\r\ntype KeytarModule = typeof import('keytar');\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\ninterface TokenRecord {\r\n accessToken: string;\r\n refreshToken: string;\r\n expiresAt?: number;\r\n}\r\n\r\ninterface ProfileConfig {\r\n id: string;\r\n cid: string;\r\n pid: string;\r\n host: string;\r\n apiHost: string;\r\n email: string;\r\n lastUsed?: number;\r\n}\r\n\r\ninterface ConfigFile {\r\n profiles: Record<string, ProfileConfig>;\r\n lastUsed?: string;\r\n}\r\n\r\ninterface LoginOptions {\r\n cid: string;\r\n pid: string;\r\n host: string;\r\n email: string;\r\n password: string;\r\n}\r\n\r\ninterface ProfileSelectionOptions {\r\n cid?: string;\r\n pid?: string;\r\n host?: string;\r\n profileId?: string;\r\n}\r\n\r\nconst SERVICE_NAME = 'beamo-node';\r\nconst CONFIG_DIR = path.join(os.homedir(), '.beamo-node');\r\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nconst TOKEN_FALLBACK_DIR = path.join(CONFIG_DIR, 'tokens');\r\nconst DEFAULT_SOCKET_HOST = 'wss://api.beamable.com/socket';\r\nconst MIN_TOKEN_BUFFER_MS = 60_000;\r\n\r\nclass CredentialStore {\r\n private keytar?: KeytarModule;\r\n\r\n private constructor(keytar?: KeytarModule) {\r\n this.keytar = keytar;\r\n }\r\n\r\n static async create(): Promise<CredentialStore> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\r\n const keytarModule: KeytarModule = (await import('keytar')).default;\r\n return new CredentialStore(keytarModule);\r\n } catch (error) {\r\n console.warn('[beamo-node] keytar not available, falling back to file-based credential storage.');\r\n return new CredentialStore();\r\n }\r\n }\r\n\r\n async get(account: string): Promise<TokenRecord | undefined> {\r\n if (this.keytar) {\r\n const payload = await this.keytar.getPassword(SERVICE_NAME, account);\r\n if (!payload) {\r\n return undefined;\r\n }\r\n return this.parseTokenPayload(payload);\r\n }\r\n\r\n const filePath = this.fallbackPath(account);\r\n try {\r\n const payload = await fs.readFile(filePath, 'utf8');\r\n return this.parseTokenPayload(payload);\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return undefined;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n async set(account: string, record: TokenRecord): Promise<void> {\r\n const payload = JSON.stringify(record);\r\n if (this.keytar) {\r\n await this.keytar.setPassword(SERVICE_NAME, account, payload);\r\n return;\r\n }\r\n\r\n await fs.mkdir(TOKEN_FALLBACK_DIR, { recursive: true });\r\n const filePath = this.fallbackPath(account);\r\n await fs.writeFile(filePath, payload, { mode: 0o600, encoding: 'utf8' });\r\n }\r\n\r\n async clear(account: string): Promise<void> {\r\n if (this.keytar) {\r\n await this.keytar.deletePassword(SERVICE_NAME, account);\r\n return;\r\n }\r\n try {\r\n await fs.unlink(this.fallbackPath(account));\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n private parseTokenPayload(payload: string): TokenRecord {\r\n const parsed = JSON.parse(payload) as Partial<TokenRecord>;\r\n if (!parsed || typeof parsed !== 'object') {\r\n throw new Error('Stored credentials are malformed.');\r\n }\r\n if (typeof parsed.accessToken !== 'string' || typeof parsed.refreshToken !== 'string') {\r\n throw new Error('Stored credentials missing access or refresh token.');\r\n }\r\n return {\r\n accessToken: parsed.accessToken,\r\n refreshToken: parsed.refreshToken,\r\n expiresAt: typeof parsed.expiresAt === 'number' ? parsed.expiresAt : undefined,\r\n };\r\n }\r\n\r\n private fallbackPath(account: string): string {\r\n const safe = Buffer.from(account).toString('base64url');\r\n return path.join(TOKEN_FALLBACK_DIR, `${safe}.json`);\r\n }\r\n}\r\n\r\nclass ConfigStore {\r\n async load(): Promise<ConfigFile> {\r\n try {\r\n const payload = await fs.readFile(CONFIG_FILE, 'utf8');\r\n const data = JSON.parse(payload) as ConfigFile;\r\n return {\r\n profiles: data?.profiles ?? {},\r\n lastUsed: data?.lastUsed,\r\n };\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return { profiles: {} };\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n async save(config: ConfigFile): Promise<void> {\r\n await fs.mkdir(CONFIG_DIR, { recursive: true });\r\n const payload = JSON.stringify(config, null, 2);\r\n await fs.writeFile(CONFIG_FILE, `${payload}\\n`, { encoding: 'utf8', mode: 0o600 });\r\n }\r\n}\r\n\r\nclass TokenManager {\r\n constructor(private readonly store: CredentialStore) {}\r\n\r\n async login(options: LoginOptions): Promise<TokenRecord> {\r\n const apiHost = normalizeApiHost(options.host);\r\n // Scope should be cid.pid if both are provided (matches official CLI behavior)\r\n const scope = options.pid ? `${options.cid}.${options.pid}` : options.cid;\r\n // Admin accounts require customerScoped: true (matches C# CLI behavior)\r\n // The registry will accept customer-scoped tokens when proper headers are used\r\n const customerScoped = true;\r\n const response = await fetch(new URL('/basic/auth/token', apiHost), {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n ...(scope ? { 'X-BEAM-SCOPE': scope } : {}),\r\n },\r\n body: JSON.stringify({\r\n grant_type: 'password',\r\n username: options.email,\r\n password: options.password,\r\n customerScoped,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const message = await safeReadError(response);\r\n throw new Error(`Login failed (${response.status}): ${message}`);\r\n }\r\n\r\n const body = (await response.json()) as Record<string, unknown>;\r\n const accessToken = typeof body.access_token === 'string' ? body.access_token : undefined;\r\n const refreshToken = typeof body.refresh_token === 'string' ? body.refresh_token : undefined;\r\n const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : Number(body.expires_in ?? 0);\r\n\r\n if (!accessToken || !refreshToken) {\r\n throw new Error('Login response did not contain access and refresh tokens.');\r\n }\r\n\r\n const expiresAt = Number.isFinite(expiresIn) && expiresIn > 0 ? Date.now() + expiresIn * 1000 : undefined;\r\n const record: TokenRecord = { accessToken, refreshToken, expiresAt };\r\n const account = profileKey({ cid: options.cid, pid: options.pid, apiHost });\r\n\r\n await this.store.set(account, record);\r\n return record;\r\n }\r\n\r\n async getValidTokens(cid: string, pid: string, host: string, forceRealmScoped = false): Promise<TokenRecord> {\r\n const apiHost = normalizeApiHost(host);\r\n const account = profileKey({ cid, pid, apiHost });\r\n\r\n const existing = await this.store.get(account);\r\n if (!existing) {\r\n throw new Error(`No stored credentials for ${cid}.${pid} (${apiHost}). Run \"beamo-node login\" first.`);\r\n }\r\n\r\n // Refresh token if expired or if explicitly requested\r\n // Note: If login was done with PID, tokens are already realm-scoped\r\n if (forceRealmScoped || isTokenExpired(existing.expiresAt)) {\r\n if (process.env.BEAMO_DEBUG === '1' || process.env.BEAMO_NODE_DEBUG === '1') {\r\n console.log(`[beamo-node] Refreshing token (forceRealmScoped=${forceRealmScoped}, expired=${isTokenExpired(existing.expiresAt)})`);\r\n }\r\n const refreshed = await this.refreshToken(existing.refreshToken, cid, pid, apiHost);\r\n await this.store.set(account, refreshed);\r\n return refreshed;\r\n }\r\n\r\n return existing;\r\n }\r\n\r\n private async refreshToken(refreshToken: string, cid: string, pid: string, apiHost: string): Promise<TokenRecord> {\r\n const scope = pid ? `${cid}.${pid}` : cid;\r\n const response = await fetch(new URL('/basic/auth/token', apiHost), {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n ...(scope ? { 'X-BEAM-SCOPE': scope } : {}),\r\n },\r\n body: JSON.stringify({\r\n grant_type: 'refresh_token',\r\n refresh_token: refreshToken,\r\n // Don't include customerScoped - refresh tokens inherit scope from the refresh token itself\r\n // Setting X-BEAM-SCOPE header should be enough to get a realm-scoped token\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const message = await safeReadError(response);\r\n throw new Error(`Failed to refresh token (${response.status}): ${message}`);\r\n }\r\n\r\n const body = (await response.json()) as Record<string, unknown>;\r\n const accessToken = typeof body.access_token === 'string' ? body.access_token : undefined;\r\n const newRefreshToken = typeof body.refresh_token === 'string' ? body.refresh_token : refreshToken;\r\n const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : Number(body.expires_in ?? 0);\r\n\r\n if (!accessToken) {\r\n throw new Error('Refresh token response missing access token.');\r\n }\r\n\r\n const expiresAt = Number.isFinite(expiresIn) && expiresIn > 0 ? Date.now() + expiresIn * 1000 : undefined;\r\n \r\n // Verify the refreshed token works with realm scope\r\n if (pid && process.env.BEAMO_DEBUG === '1') {\r\n try {\r\n const testUrl = new URL('/basic/accounts/me', apiHost);\r\n const testResponse = await fetch(testUrl, {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': scope,\r\n },\r\n });\r\n if (!testResponse.ok) {\r\n console.warn(`[beamo-node] Warning: Refreshed token validation failed (${testResponse.status}). Token may still be customer-scoped.`);\r\n }\r\n } catch {\r\n // Ignore validation errors in debug mode\r\n }\r\n }\r\n \r\n return { accessToken, refreshToken: newRefreshToken, expiresAt };\r\n }\r\n}\r\n\r\nfunction parseCommandLine(argv: string[]): { command?: string; args: string[] } {\r\n const [command, ...rest] = argv;\r\n return { command, args: rest };\r\n}\r\n\r\nfunction splitArgs(args: string[], recognizedFlags: Set<string>): { flags: Record<string, string | boolean>; forward: string[] } {\r\n const flags: Record<string, string | boolean> = {};\r\n const forward: string[] = [];\r\n\r\n for (let index = 0; index < args.length; index += 1) {\r\n const token = args[index];\r\n if (!token.startsWith('-')) {\r\n forward.push(token);\r\n continue;\r\n }\r\n\r\n const key = token.replace(/^--/, '');\r\n\r\n if (recognizedFlags.has(key)) {\r\n const next = args[index + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n flags[key] = next;\r\n index += 1;\r\n } else {\r\n flags[key] = true;\r\n }\r\n } else {\r\n forward.push(token);\r\n const next = args[index + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n forward.push(next);\r\n index += 1;\r\n }\r\n }\r\n }\r\n\r\n return { flags, forward };\r\n}\r\n\r\nasync function promptInput(question: string, defaultValue?: string): Promise<string> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout });\r\n return new Promise<string>((resolve) => {\r\n rl.question(defaultValue ? `${question} (${defaultValue}): ` : `${question}: `, (answer) => {\r\n rl.close();\r\n const value = answer.trim();\r\n resolve(value === '' && defaultValue !== undefined ? defaultValue : value);\r\n });\r\n });\r\n}\r\n\r\nasync function promptHidden(question: string): Promise<string> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout } as readline.ReadLineOptions & { stdoutMuted?: boolean });\r\n\r\n return new Promise<string>((resolve) => {\r\n const mutable = rl as readline.Interface & {\r\n stdoutMuted?: boolean;\r\n _writeToOutput?: (stringToWrite: string) => void;\r\n output?: typeof stdout;\r\n };\r\n mutable.stdoutMuted = true;\r\n const originalWrite = mutable._writeToOutput?.bind(mutable);\r\n const outputStream = mutable.output ?? stdout;\r\n mutable._writeToOutput = (stringToWrite: string) => {\r\n if (mutable.stdoutMuted) {\r\n outputStream.write('*');\r\n } else if (originalWrite) {\r\n originalWrite(stringToWrite);\r\n } else {\r\n outputStream.write(stringToWrite);\r\n }\r\n };\r\n\r\n mutable.question(`${question}: `, (answer) => {\r\n mutable.close();\r\n stdout.write(os.EOL);\r\n resolve(answer);\r\n });\r\n });\r\n}\r\n\r\nasync function handleLogin(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags } = splitArgs(args, new Set(['cid', 'pid', 'host', 'email']));\r\n const envDefaults = await loadEnvDefaults();\r\n\r\n const cid = typeof flags.cid === 'string' ? flags.cid : await promptInput('CID', envDefaults.cid);\r\n const pid = typeof flags.pid === 'string' ? flags.pid : await promptInput('PID', envDefaults.pid);\r\n const hostDefault = envDefaults.host ?? DEFAULT_SOCKET_HOST;\r\n const host = typeof flags.host === 'string' ? flags.host : await promptInput('Host', hostDefault);\r\n const email = typeof flags.email === 'string' ? flags.email : await promptInput('Email');\r\n const password = await promptHidden('Password');\r\n\r\n if (!cid || !pid || !host || !email || !password) {\r\n throw new Error('CID, PID, host, email, and password are required.');\r\n }\r\n\r\n const record = await tokenManager.login({ cid, pid, host, email, password });\r\n const config = await configStore.load();\r\n const apiHost = normalizeApiHost(host);\r\n const id = profileKey({ cid, pid, apiHost });\r\n\r\n config.profiles[id] = {\r\n id,\r\n cid,\r\n pid,\r\n host,\r\n apiHost,\r\n email,\r\n lastUsed: Date.now(),\r\n };\r\n config.lastUsed = id;\r\n\r\n await configStore.save(config);\r\n\r\n console.log(`Login successful for ${email} (${cid}.${pid}).`);\r\n if (!record.expiresAt) {\r\n console.warn('[beamo-node] Access token does not include an expiration. You may need to re-login if publish fails.');\r\n }\r\n}\r\n\r\nasync function handlePublish(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags, forward } = splitArgs(args, new Set(['cid', 'pid', 'host', 'profile']));\r\n const profile = await resolveProfile(flags, configStore);\r\n\r\n // Refresh token if expired (customer-scoped tokens work with registry when proper headers are used)\r\n // The C# CLI uses the same access token for both registry API calls and Docker registry uploads\r\n const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host, false);\r\n const env = buildCommandEnv(profile, tokens);\r\n\r\n const scriptArgs = buildScriptArgs(forward, profile, true); // publish needs --api-host\r\n const scriptPath = path.resolve(__dirname, '../../scripts/publish-service.mjs');\r\n await runScript(scriptPath, scriptArgs, env);\r\n}\r\n\r\nasync function handleValidate(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags, forward } = splitArgs(args, new Set(['cid', 'pid', 'host', 'profile']));\r\n const profile = await resolveProfile(flags, configStore);\r\n\r\n // Validation does not require tokens, but downstream scripts expect CID/PID/HOST env values.\r\n const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host);\r\n const env = buildCommandEnv(profile, tokens);\r\n\r\n const scriptArgs = buildScriptArgs(forward, profile, false); // validate doesn't need --api-host\r\n const scriptPath = path.resolve(__dirname, '../../scripts/validate-service.mjs');\r\n await runScript(scriptPath, scriptArgs, env);\r\n}\r\n\r\nasync function resolveProfile(flags: Record<string, string | boolean>, configStore: ConfigStore): Promise<ProfileConfig> {\r\n const config = await configStore.load();\r\n\r\n if (typeof flags.profile === 'string') {\r\n const explicit = config.profiles[flags.profile];\r\n if (!explicit) {\r\n throw new Error(`Profile \"${flags.profile}\" not found. Run \"beamo-node login\" to create it.`);\r\n }\r\n config.lastUsed = explicit.id;\r\n await configStore.save(config);\r\n return explicit;\r\n }\r\n\r\n const directOptions: ProfileSelectionOptions = {\r\n cid: typeof flags.cid === 'string' ? flags.cid : undefined,\r\n pid: typeof flags.pid === 'string' ? flags.pid : undefined,\r\n host: typeof flags.host === 'string' ? flags.host : undefined,\r\n };\r\n\r\n if (directOptions.cid && directOptions.pid && directOptions.host) {\r\n const apiHost = normalizeApiHost(directOptions.host);\r\n const id = profileKey({ cid: directOptions.cid, pid: directOptions.pid, apiHost });\r\n const profile = config.profiles[id];\r\n if (!profile) {\r\n throw new Error(`No stored credentials for ${directOptions.cid}.${directOptions.pid}. Run \"beamo-node login\" first.`);\r\n }\r\n profile.lastUsed = Date.now();\r\n config.lastUsed = profile.id;\r\n await configStore.save(config);\r\n return profile;\r\n }\r\n\r\n if (!config.lastUsed) {\r\n if (Object.keys(config.profiles).length === 0) {\r\n throw new Error('No stored credentials found. Run \"beamo-node login\" first.');\r\n }\r\n throw new Error('Multiple profiles found. Specify one with \"--profile <id>\" or run \"beamo-node login\" first.');\r\n }\r\n\r\n const profile = config.profiles[config.lastUsed];\r\n if (!profile) {\r\n throw new Error(`Last used profile \"${config.lastUsed}\" is missing. Run \"beamo-node login\" to refresh credentials.`);\r\n }\r\n\r\n profile.lastUsed = Date.now();\r\n config.lastUsed = profile.id;\r\n await configStore.save(config);\r\n return profile;\r\n}\r\n\r\nfunction buildScriptArgs(original: string[], profile: ProfileConfig, includeApiHost = true): string[] {\r\n const hasFlag = (flag: string) => original.some((token) => token === flag);\r\n const args: string[] = [...original];\r\n if (!hasFlag('--cid')) {\r\n args.push('--cid', profile.cid);\r\n }\r\n if (!hasFlag('--pid')) {\r\n args.push('--pid', profile.pid);\r\n }\r\n if (!hasFlag('--host')) {\r\n args.push('--host', profile.host);\r\n }\r\n if (includeApiHost && !hasFlag('--api-host')) {\r\n args.push('--api-host', profile.apiHost);\r\n }\r\n return args;\r\n}\r\n\r\nfunction buildCommandEnv(profile: ProfileConfig, tokens: TokenRecord): NodeJS.ProcessEnv {\r\n return {\r\n ...process.env,\r\n BEAMABLE_TOKEN: tokens.accessToken,\r\n ACCESS_TOKEN: tokens.accessToken,\r\n BEAMABLE_REFRESH_TOKEN: tokens.refreshToken,\r\n REFRESH_TOKEN: tokens.refreshToken,\r\n BEAMABLE_CID: profile.cid,\r\n CID: profile.cid,\r\n BEAMABLE_PID: profile.pid,\r\n PID: profile.pid,\r\n BEAMABLE_HOST: profile.host,\r\n HOST: profile.host,\r\n BEAMABLE_API_HOST: profile.apiHost,\r\n };\r\n}\r\n\r\nasync function runScript(command: string, args: string[], env: NodeJS.ProcessEnv): Promise<void> {\r\n const isNodeScript = /\\.(mjs|cjs|js)$/i.test(command);\r\n const executable = isNodeScript ? process.execPath : command;\r\n const finalArgs = isNodeScript ? [command, ...args] : args;\r\n const shouldUseShell = process.platform === 'win32' && !isNodeScript;\r\n const debug = process.env.BEAMO_DEBUG === '1' || process.env.BEAMO_NODE_DEBUG === '1';\r\n if (debug) {\r\n console.log(\r\n `[beamo-node] command=\"${command}\" exec=\"${executable}\" args=${JSON.stringify(\r\n finalArgs,\r\n )} shell=${shouldUseShell} cwd=${process.cwd()}`,\r\n );\r\n }\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const child = spawn(executable, finalArgs, {\r\n stdio: 'inherit',\r\n env,\r\n shell: shouldUseShell,\r\n });\r\n\r\n child.on('error', (error) => reject(error));\r\n child.on('exit', (code) => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`Command failed with exit code ${code ?? 'unknown'}.`));\r\n }\r\n });\r\n });\r\n}\r\n\r\nfunction normalizeApiHost(host: string): string {\r\n const trimmed = host.trim();\r\n if (trimmed.startsWith('wss://')) {\r\n return `https://${trimmed.substring('wss://'.length).replace(/\\/socket$/, '')}`;\r\n }\r\n if (trimmed.startsWith('ws://')) {\r\n return `http://${trimmed.substring('ws://'.length).replace(/\\/socket$/, '')}`;\r\n }\r\n let normalized = trimmed.replace(/\\/socket$/, '').replace(/\\/$/, '');\r\n if (!/^https?:\\/\\//i.test(normalized)) {\r\n normalized = `https://${normalized}`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction profileKey(input: { cid: string; pid: string; apiHost: string }): string {\r\n return `${input.cid}.${input.pid}@${input.apiHost}`;\r\n}\r\n\r\nfunction isTokenExpired(expiresAt?: number): boolean {\r\n if (!expiresAt || !Number.isFinite(expiresAt)) {\r\n return true;\r\n }\r\n return Date.now() + MIN_TOKEN_BUFFER_MS >= expiresAt;\r\n}\r\n\r\nasync function safeReadError(response: Response): Promise<string> {\r\n try {\r\n const body = await response.text();\r\n if (!body) {\r\n return 'No response body';\r\n }\r\n try {\r\n const parsed = JSON.parse(body) as Record<string, unknown>;\r\n return typeof parsed.error === 'string'\r\n ? parsed.error\r\n : typeof parsed.message === 'string'\r\n ? parsed.message\r\n : body;\r\n } catch {\r\n return body;\r\n }\r\n } catch {\r\n return 'No response body';\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const [, , ...argv] = process.argv;\r\n const { command, args } = parseCommandLine(argv);\r\n\r\n if (!command || command === '--help' || command === '-h') {\r\n printHelp();\r\n return;\r\n }\r\n\r\n const credentialStore = await CredentialStore.create();\r\n const tokenManager = new TokenManager(credentialStore);\r\n const configStore = new ConfigStore();\r\n\r\n switch (command) {\r\n case 'login':\r\n await handleLogin(args, configStore, tokenManager);\r\n break;\r\n case 'publish':\r\n await handlePublish(args, configStore, tokenManager);\r\n break;\r\n case 'validate':\r\n await handleValidate(args, configStore, tokenManager);\r\n break;\r\n case 'profiles':\r\n await listProfiles(configStore);\r\n break;\r\n default:\r\n console.error(`Unknown command: ${command}`);\r\n printHelp();\r\n exit(1);\r\n }\r\n}\r\n\r\nasync function listProfiles(configStore: ConfigStore): Promise<void> {\r\n const config = await configStore.load();\r\n const entries = Object.values(config.profiles).sort((a, b) => (b.lastUsed ?? 0) - (a.lastUsed ?? 0));\r\n if (entries.length === 0) {\r\n console.log('No stored profiles. Run \"beamo-node login\" to create one.');\r\n return;\r\n }\r\n console.log('Stored profiles:');\r\n for (const profile of entries) {\r\n const marker = config.lastUsed === profile.id ? '*' : ' ';\r\n console.log(` ${marker} ${profile.id}`);\r\n console.log(` CID: ${profile.cid}`);\r\n console.log(` PID: ${profile.pid}`);\r\n console.log(` Host: ${profile.host}`);\r\n console.log(` Email: ${profile.email}`);\r\n }\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(`beamo-node CLI\r\n\r\nUsage:\r\n beamo-node login [--cid CID] [--pid PID] [--host HOST] [--email EMAIL]\r\n beamo-node publish [--profile ID] [--cid CID --pid PID --host HOST] [-- ...publish flags...]\r\n beamo-node validate [--profile ID] [--cid CID --pid PID --host HOST] [-- ...validate flags...]\r\n beamo-node profiles\r\n\r\nCommands:\r\n login Authenticate with Beamable and store credentials securely.\r\n publish Run the publish pipeline using stored credentials (forwards extra flags to the publish script).\r\n validate Generate OpenAPI docs and validate the project (forwards extra flags).\r\n profiles List stored profiles and indicate the default selection.\r\n`);\r\n}\r\n\r\nmain().catch((error) => {\r\n console.error(error instanceof Error ? error.message : error);\r\n exit(1);\r\n});\r\n\r\nasync function loadEnvDefaults(): Promise<ProfileSelectionOptions> {\r\n const envPath = path.resolve('.env');\r\n try {\r\n const content = await fs.readFile(envPath, 'utf8');\r\n const parsed = dotenv.parse(content);\r\n const cid = parsed.BEAMABLE_CID ?? parsed.CID ?? undefined;\r\n const pid = parsed.BEAMABLE_PID ?? parsed.PID ?? undefined;\r\n const host = parsed.BEAMABLE_HOST ?? parsed.HOST ?? undefined;\r\n return {\r\n cid: cid?.trim() || undefined,\r\n pid: pid?.trim() || undefined,\r\n host: host?.trim() || undefined,\r\n };\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return {};\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n\r\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\r\n\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport os from 'node:os';\r\nimport { spawn } from 'node:child_process';\r\nimport readline from 'node:readline';\r\nimport { stdin, stdout, exit } from 'node:process';\r\nimport { fileURLToPath } from 'node:url';\r\nimport dotenv from 'dotenv';\r\n\r\ntype KeytarModule = typeof import('keytar');\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\ninterface TokenRecord {\r\n accessToken: string;\r\n refreshToken: string;\r\n expiresAt?: number;\r\n}\r\n\r\ninterface ProfileConfig {\r\n id: string;\r\n cid: string;\r\n pid: string;\r\n host: string;\r\n apiHost: string;\r\n email: string;\r\n lastUsed?: number;\r\n}\r\n\r\ninterface ConfigFile {\r\n profiles: Record<string, ProfileConfig>;\r\n lastUsed?: string;\r\n}\r\n\r\ninterface LoginOptions {\r\n cid: string;\r\n pid: string;\r\n host: string;\r\n email: string;\r\n password: string;\r\n}\r\n\r\ninterface ProfileSelectionOptions {\r\n cid?: string;\r\n pid?: string;\r\n host?: string;\r\n profileId?: string;\r\n}\r\n\r\nconst SERVICE_NAME = 'beamo-node';\r\nconst CONFIG_DIR = path.join(os.homedir(), '.beamo-node');\r\nconst CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');\r\nconst TOKEN_FALLBACK_DIR = path.join(CONFIG_DIR, 'tokens');\r\nconst DEFAULT_SOCKET_HOST = 'wss://api.beamable.com/socket';\r\nconst MIN_TOKEN_BUFFER_MS = 60_000;\r\n\r\nclass CredentialStore {\r\n private keytar?: KeytarModule;\r\n\r\n private constructor(keytar?: KeytarModule) {\r\n this.keytar = keytar;\r\n }\r\n\r\n static async create(): Promise<CredentialStore> {\r\n try {\r\n // eslint-disable-next-line @typescript-eslint/consistent-type-imports\r\n const keytarModule: KeytarModule = (await import('keytar')).default;\r\n return new CredentialStore(keytarModule);\r\n } catch (error) {\r\n console.warn('[beamo-node] keytar not available, falling back to file-based credential storage.');\r\n return new CredentialStore();\r\n }\r\n }\r\n\r\n async get(account: string): Promise<TokenRecord | undefined> {\r\n if (this.keytar) {\r\n const payload = await this.keytar.getPassword(SERVICE_NAME, account);\r\n if (!payload) {\r\n return undefined;\r\n }\r\n return this.parseTokenPayload(payload);\r\n }\r\n\r\n const filePath = this.fallbackPath(account);\r\n try {\r\n const payload = await fs.readFile(filePath, 'utf8');\r\n return this.parseTokenPayload(payload);\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return undefined;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n async set(account: string, record: TokenRecord): Promise<void> {\r\n const payload = JSON.stringify(record);\r\n if (this.keytar) {\r\n await this.keytar.setPassword(SERVICE_NAME, account, payload);\r\n return;\r\n }\r\n\r\n await fs.mkdir(TOKEN_FALLBACK_DIR, { recursive: true });\r\n const filePath = this.fallbackPath(account);\r\n await fs.writeFile(filePath, payload, { mode: 0o600, encoding: 'utf8' });\r\n }\r\n\r\n async clear(account: string): Promise<void> {\r\n if (this.keytar) {\r\n await this.keytar.deletePassword(SERVICE_NAME, account);\r\n return;\r\n }\r\n try {\r\n await fs.unlink(this.fallbackPath(account));\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n private parseTokenPayload(payload: string): TokenRecord {\r\n const parsed = JSON.parse(payload) as Partial<TokenRecord>;\r\n if (!parsed || typeof parsed !== 'object') {\r\n throw new Error('Stored credentials are malformed.');\r\n }\r\n if (typeof parsed.accessToken !== 'string' || typeof parsed.refreshToken !== 'string') {\r\n throw new Error('Stored credentials missing access or refresh token.');\r\n }\r\n return {\r\n accessToken: parsed.accessToken,\r\n refreshToken: parsed.refreshToken,\r\n expiresAt: typeof parsed.expiresAt === 'number' ? parsed.expiresAt : undefined,\r\n };\r\n }\r\n\r\n private fallbackPath(account: string): string {\r\n const safe = Buffer.from(account).toString('base64url');\r\n return path.join(TOKEN_FALLBACK_DIR, `${safe}.json`);\r\n }\r\n}\r\n\r\nclass ConfigStore {\r\n async load(): Promise<ConfigFile> {\r\n try {\r\n const payload = await fs.readFile(CONFIG_FILE, 'utf8');\r\n const data = JSON.parse(payload) as ConfigFile;\r\n return {\r\n profiles: data?.profiles ?? {},\r\n lastUsed: data?.lastUsed,\r\n };\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return { profiles: {} };\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n async save(config: ConfigFile): Promise<void> {\r\n await fs.mkdir(CONFIG_DIR, { recursive: true });\r\n const payload = JSON.stringify(config, null, 2);\r\n await fs.writeFile(CONFIG_FILE, `${payload}\\n`, { encoding: 'utf8', mode: 0o600 });\r\n }\r\n}\r\n\r\nclass TokenManager {\r\n constructor(private readonly store: CredentialStore) {}\r\n\r\n async login(options: LoginOptions): Promise<TokenRecord> {\r\n const apiHost = normalizeApiHost(options.host);\r\n // Scope should be cid.pid if both are provided (matches official CLI behavior)\r\n const scope = options.pid ? `${options.cid}.${options.pid}` : options.cid;\r\n // Admin accounts require customerScoped: true (matches C# CLI behavior)\r\n // The registry will accept customer-scoped tokens when proper headers are used\r\n const customerScoped = true;\r\n const response = await fetch(new URL('/basic/auth/token', apiHost), {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n ...(scope ? { 'X-BEAM-SCOPE': scope } : {}),\r\n },\r\n body: JSON.stringify({\r\n grant_type: 'password',\r\n username: options.email,\r\n password: options.password,\r\n customerScoped,\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const message = await safeReadError(response);\r\n throw new Error(`Login failed (${response.status}): ${message}`);\r\n }\r\n\r\n const body = (await response.json()) as Record<string, unknown>;\r\n const accessToken = typeof body.access_token === 'string' ? body.access_token : undefined;\r\n const refreshToken = typeof body.refresh_token === 'string' ? body.refresh_token : undefined;\r\n const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : Number(body.expires_in ?? 0);\r\n\r\n if (!accessToken || !refreshToken) {\r\n throw new Error('Login response did not contain access and refresh tokens.');\r\n }\r\n\r\n const expiresAt = Number.isFinite(expiresIn) && expiresIn > 0 ? Date.now() + expiresIn * 1000 : undefined;\r\n const record: TokenRecord = { accessToken, refreshToken, expiresAt };\r\n const account = profileKey({ cid: options.cid, pid: options.pid, apiHost });\r\n\r\n await this.store.set(account, record);\r\n return record;\r\n }\r\n\r\n async getValidTokens(cid: string, pid: string, host: string, forceRealmScoped = false): Promise<TokenRecord> {\r\n const apiHost = normalizeApiHost(host);\r\n const account = profileKey({ cid, pid, apiHost });\r\n\r\n const existing = await this.store.get(account);\r\n if (!existing) {\r\n throw new Error(`No stored credentials for ${cid}.${pid} (${apiHost}). Run \"beamo-node login\" first.`);\r\n }\r\n\r\n // Refresh token if expired or if explicitly requested\r\n // Note: If login was done with PID, tokens are already realm-scoped\r\n if (forceRealmScoped || isTokenExpired(existing.expiresAt)) {\r\n if (process.env.BEAMO_DEBUG === '1' || process.env.BEAMO_NODE_DEBUG === '1') {\r\n console.log(`[beamo-node] Refreshing token (forceRealmScoped=${forceRealmScoped}, expired=${isTokenExpired(existing.expiresAt)})`);\r\n }\r\n const refreshed = await this.refreshToken(existing.refreshToken, cid, pid, apiHost);\r\n await this.store.set(account, refreshed);\r\n return refreshed;\r\n }\r\n\r\n return existing;\r\n }\r\n\r\n private async refreshToken(refreshToken: string, cid: string, pid: string, apiHost: string): Promise<TokenRecord> {\r\n const scope = pid ? `${cid}.${pid}` : cid;\r\n const response = await fetch(new URL('/basic/auth/token', apiHost), {\r\n method: 'POST',\r\n headers: {\r\n Accept: 'application/json',\r\n 'Content-Type': 'application/json',\r\n ...(scope ? { 'X-BEAM-SCOPE': scope } : {}),\r\n },\r\n body: JSON.stringify({\r\n grant_type: 'refresh_token',\r\n refresh_token: refreshToken,\r\n // Don't include customerScoped - refresh tokens inherit scope from the refresh token itself\r\n // Setting X-BEAM-SCOPE header should be enough to get a realm-scoped token\r\n }),\r\n });\r\n\r\n if (!response.ok) {\r\n const message = await safeReadError(response);\r\n throw new Error(`Failed to refresh token (${response.status}): ${message}`);\r\n }\r\n\r\n const body = (await response.json()) as Record<string, unknown>;\r\n const accessToken = typeof body.access_token === 'string' ? body.access_token : undefined;\r\n const newRefreshToken = typeof body.refresh_token === 'string' ? body.refresh_token : refreshToken;\r\n const expiresIn = typeof body.expires_in === 'number' ? body.expires_in : Number(body.expires_in ?? 0);\r\n\r\n if (!accessToken) {\r\n throw new Error('Refresh token response missing access token.');\r\n }\r\n\r\n const expiresAt = Number.isFinite(expiresIn) && expiresIn > 0 ? Date.now() + expiresIn * 1000 : undefined;\r\n \r\n // Verify the refreshed token works with realm scope\r\n if (pid && process.env.BEAMO_DEBUG === '1') {\r\n try {\r\n const testUrl = new URL('/basic/accounts/me', apiHost);\r\n const testResponse = await fetch(testUrl, {\r\n headers: {\r\n Authorization: `Bearer ${accessToken}`,\r\n Accept: 'application/json',\r\n 'X-BEAM-SCOPE': scope,\r\n },\r\n });\r\n if (!testResponse.ok) {\r\n console.warn(`[beamo-node] Warning: Refreshed token validation failed (${testResponse.status}). Token may still be customer-scoped.`);\r\n }\r\n } catch {\r\n // Ignore validation errors in debug mode\r\n }\r\n }\r\n \r\n return { accessToken, refreshToken: newRefreshToken, expiresAt };\r\n }\r\n}\r\n\r\nfunction parseCommandLine(argv: string[]): { command?: string; args: string[] } {\r\n const [command, ...rest] = argv;\r\n return { command, args: rest };\r\n}\r\n\r\nfunction splitArgs(args: string[], recognizedFlags: Set<string>): { flags: Record<string, string | boolean>; forward: string[] } {\r\n const flags: Record<string, string | boolean> = {};\r\n const forward: string[] = [];\r\n\r\n for (let index = 0; index < args.length; index += 1) {\r\n const token = args[index];\r\n if (!token.startsWith('-')) {\r\n forward.push(token);\r\n continue;\r\n }\r\n\r\n const key = token.replace(/^--/, '');\r\n\r\n if (recognizedFlags.has(key)) {\r\n const next = args[index + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n flags[key] = next;\r\n index += 1;\r\n } else {\r\n flags[key] = true;\r\n }\r\n } else {\r\n forward.push(token);\r\n const next = args[index + 1];\r\n if (next !== undefined && !next.startsWith('-')) {\r\n forward.push(next);\r\n index += 1;\r\n }\r\n }\r\n }\r\n\r\n return { flags, forward };\r\n}\r\n\r\nasync function promptInput(question: string, defaultValue?: string): Promise<string> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout });\r\n return new Promise<string>((resolve) => {\r\n rl.question(defaultValue ? `${question} (${defaultValue}): ` : `${question}: `, (answer) => {\r\n rl.close();\r\n const value = answer.trim();\r\n resolve(value === '' && defaultValue !== undefined ? defaultValue : value);\r\n });\r\n });\r\n}\r\n\r\nasync function promptHidden(question: string): Promise<string> {\r\n const rl = readline.createInterface({ input: stdin, output: stdout } as readline.ReadLineOptions & { stdoutMuted?: boolean });\r\n\r\n return new Promise<string>((resolve) => {\r\n const mutable = rl as readline.Interface & {\r\n stdoutMuted?: boolean;\r\n _writeToOutput?: (stringToWrite: string) => void;\r\n output?: typeof stdout;\r\n };\r\n mutable.stdoutMuted = true;\r\n const originalWrite = mutable._writeToOutput?.bind(mutable);\r\n const outputStream = mutable.output ?? stdout;\r\n mutable._writeToOutput = (stringToWrite: string) => {\r\n if (mutable.stdoutMuted) {\r\n outputStream.write('*');\r\n } else if (originalWrite) {\r\n originalWrite(stringToWrite);\r\n } else {\r\n outputStream.write(stringToWrite);\r\n }\r\n };\r\n\r\n mutable.question(`${question}: `, (answer) => {\r\n mutable.close();\r\n stdout.write(os.EOL);\r\n resolve(answer);\r\n });\r\n });\r\n}\r\n\r\nasync function handleLogin(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags } = splitArgs(args, new Set(['cid', 'pid', 'host', 'email']));\r\n const envDefaults = await loadEnvDefaults();\r\n\r\n const cid = typeof flags.cid === 'string' ? flags.cid : await promptInput('CID', envDefaults.cid);\r\n const pid = typeof flags.pid === 'string' ? flags.pid : await promptInput('PID', envDefaults.pid);\r\n const hostDefault = envDefaults.host ?? DEFAULT_SOCKET_HOST;\r\n const host = typeof flags.host === 'string' ? flags.host : await promptInput('Host', hostDefault);\r\n const email = typeof flags.email === 'string' ? flags.email : await promptInput('Email');\r\n const password = await promptHidden('Password');\r\n\r\n if (!cid || !pid || !host || !email || !password) {\r\n throw new Error('CID, PID, host, email, and password are required.');\r\n }\r\n\r\n const record = await tokenManager.login({ cid, pid, host, email, password });\r\n const config = await configStore.load();\r\n const apiHost = normalizeApiHost(host);\r\n const id = profileKey({ cid, pid, apiHost });\r\n\r\n config.profiles[id] = {\r\n id,\r\n cid,\r\n pid,\r\n host,\r\n apiHost,\r\n email,\r\n lastUsed: Date.now(),\r\n };\r\n config.lastUsed = id;\r\n\r\n await configStore.save(config);\r\n\r\n console.log(`Login successful for ${email} (${cid}.${pid}).`);\r\n if (!record.expiresAt) {\r\n console.warn('[beamo-node] Access token does not include an expiration. You may need to re-login if publish fails.');\r\n }\r\n}\r\n\r\nasync function handlePublish(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags, forward } = splitArgs(args, new Set(['cid', 'pid', 'host', 'profile']));\r\n const profile = await resolveProfile(flags, configStore);\r\n\r\n // Refresh token if expired (customer-scoped tokens work with registry when proper headers are used)\r\n // The C# CLI uses the same access token for both registry API calls and Docker registry uploads\r\n const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host, false);\r\n const env = buildCommandEnv(profile, tokens);\r\n\r\n const scriptArgs = buildScriptArgs(forward, profile, true); // publish needs --api-host\r\n \r\n // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided\r\n const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));\r\n if (!hasEnvFileFlag) {\r\n const envPath = path.resolve('.env');\r\n try {\r\n await fs.access(envPath);\r\n scriptArgs.push('--env-file', '.env');\r\n } catch {\r\n // .env file doesn't exist, that's okay\r\n }\r\n }\r\n \r\n const scriptPath = path.resolve(__dirname, '../../scripts/publish-service.mjs');\r\n await runScript(scriptPath, scriptArgs, env);\r\n}\r\n\r\nasync function handleValidate(args: string[], configStore: ConfigStore, tokenManager: TokenManager): Promise<void> {\r\n const { flags, forward } = splitArgs(args, new Set(['cid', 'pid', 'host', 'profile']));\r\n const profile = await resolveProfile(flags, configStore);\r\n\r\n // Validation does not require tokens, but downstream scripts expect CID/PID/HOST env values.\r\n const tokens = await tokenManager.getValidTokens(profile.cid, profile.pid, profile.host);\r\n const env = buildCommandEnv(profile, tokens);\r\n\r\n const scriptArgs = buildScriptArgs(forward, profile, false); // validate doesn't need --api-host\r\n \r\n // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided\r\n const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));\r\n if (!hasEnvFileFlag) {\r\n const envPath = path.resolve('.env');\r\n try {\r\n await fs.access(envPath);\r\n scriptArgs.push('--env-file', '.env');\r\n } catch {\r\n // .env file doesn't exist, that's okay\r\n }\r\n }\r\n \r\n const scriptPath = path.resolve(__dirname, '../../scripts/validate-service.mjs');\r\n await runScript(scriptPath, scriptArgs, env);\r\n}\r\n\r\nasync function resolveProfile(flags: Record<string, string | boolean>, configStore: ConfigStore): Promise<ProfileConfig> {\r\n const config = await configStore.load();\r\n\r\n if (typeof flags.profile === 'string') {\r\n const explicit = config.profiles[flags.profile];\r\n if (!explicit) {\r\n throw new Error(`Profile \"${flags.profile}\" not found. Run \"beamo-node login\" to create it.`);\r\n }\r\n config.lastUsed = explicit.id;\r\n await configStore.save(config);\r\n return explicit;\r\n }\r\n\r\n const directOptions: ProfileSelectionOptions = {\r\n cid: typeof flags.cid === 'string' ? flags.cid : undefined,\r\n pid: typeof flags.pid === 'string' ? flags.pid : undefined,\r\n host: typeof flags.host === 'string' ? flags.host : undefined,\r\n };\r\n\r\n if (directOptions.cid && directOptions.pid && directOptions.host) {\r\n const apiHost = normalizeApiHost(directOptions.host);\r\n const id = profileKey({ cid: directOptions.cid, pid: directOptions.pid, apiHost });\r\n const profile = config.profiles[id];\r\n if (!profile) {\r\n throw new Error(`No stored credentials for ${directOptions.cid}.${directOptions.pid}. Run \"beamo-node login\" first.`);\r\n }\r\n profile.lastUsed = Date.now();\r\n config.lastUsed = profile.id;\r\n await configStore.save(config);\r\n return profile;\r\n }\r\n\r\n if (!config.lastUsed) {\r\n if (Object.keys(config.profiles).length === 0) {\r\n throw new Error('No stored credentials found. Run \"beamo-node login\" first.');\r\n }\r\n throw new Error('Multiple profiles found. Specify one with \"--profile <id>\" or run \"beamo-node login\" first.');\r\n }\r\n\r\n const profile = config.profiles[config.lastUsed];\r\n if (!profile) {\r\n throw new Error(`Last used profile \"${config.lastUsed}\" is missing. Run \"beamo-node login\" to refresh credentials.`);\r\n }\r\n\r\n profile.lastUsed = Date.now();\r\n config.lastUsed = profile.id;\r\n await configStore.save(config);\r\n return profile;\r\n}\r\n\r\nfunction buildScriptArgs(original: string[], profile: ProfileConfig, includeApiHost = true): string[] {\r\n const hasFlag = (flag: string) => original.some((token) => token === flag);\r\n const args: string[] = [...original];\r\n if (!hasFlag('--cid')) {\r\n args.push('--cid', profile.cid);\r\n }\r\n if (!hasFlag('--pid')) {\r\n args.push('--pid', profile.pid);\r\n }\r\n if (!hasFlag('--host')) {\r\n args.push('--host', profile.host);\r\n }\r\n if (includeApiHost && !hasFlag('--api-host')) {\r\n args.push('--api-host', profile.apiHost);\r\n }\r\n return args;\r\n}\r\n\r\nfunction buildCommandEnv(profile: ProfileConfig, tokens: TokenRecord): NodeJS.ProcessEnv {\r\n return {\r\n ...process.env,\r\n BEAMABLE_TOKEN: tokens.accessToken,\r\n ACCESS_TOKEN: tokens.accessToken,\r\n BEAMABLE_REFRESH_TOKEN: tokens.refreshToken,\r\n REFRESH_TOKEN: tokens.refreshToken,\r\n BEAMABLE_CID: profile.cid,\r\n CID: profile.cid,\r\n BEAMABLE_PID: profile.pid,\r\n PID: profile.pid,\r\n BEAMABLE_HOST: profile.host,\r\n HOST: profile.host,\r\n BEAMABLE_API_HOST: profile.apiHost,\r\n };\r\n}\r\n\r\nasync function runScript(command: string, args: string[], env: NodeJS.ProcessEnv): Promise<void> {\r\n const isNodeScript = /\\.(mjs|cjs|js)$/i.test(command);\r\n const executable = isNodeScript ? process.execPath : command;\r\n const finalArgs = isNodeScript ? [command, ...args] : args;\r\n const shouldUseShell = process.platform === 'win32' && !isNodeScript;\r\n const debug = process.env.BEAMO_DEBUG === '1' || process.env.BEAMO_NODE_DEBUG === '1';\r\n if (debug) {\r\n console.log(\r\n `[beamo-node] command=\"${command}\" exec=\"${executable}\" args=${JSON.stringify(\r\n finalArgs,\r\n )} shell=${shouldUseShell} cwd=${process.cwd()}`,\r\n );\r\n }\r\n\r\n await new Promise<void>((resolve, reject) => {\r\n const child = spawn(executable, finalArgs, {\r\n stdio: 'inherit',\r\n env,\r\n shell: shouldUseShell,\r\n });\r\n\r\n child.on('error', (error) => reject(error));\r\n child.on('exit', (code) => {\r\n if (code === 0) {\r\n resolve();\r\n } else {\r\n reject(new Error(`Command failed with exit code ${code ?? 'unknown'}.`));\r\n }\r\n });\r\n });\r\n}\r\n\r\nfunction normalizeApiHost(host: string): string {\r\n const trimmed = host.trim();\r\n if (trimmed.startsWith('wss://')) {\r\n return `https://${trimmed.substring('wss://'.length).replace(/\\/socket$/, '')}`;\r\n }\r\n if (trimmed.startsWith('ws://')) {\r\n return `http://${trimmed.substring('ws://'.length).replace(/\\/socket$/, '')}`;\r\n }\r\n let normalized = trimmed.replace(/\\/socket$/, '').replace(/\\/$/, '');\r\n if (!/^https?:\\/\\//i.test(normalized)) {\r\n normalized = `https://${normalized}`;\r\n }\r\n return normalized;\r\n}\r\n\r\nfunction profileKey(input: { cid: string; pid: string; apiHost: string }): string {\r\n return `${input.cid}.${input.pid}@${input.apiHost}`;\r\n}\r\n\r\nfunction isTokenExpired(expiresAt?: number): boolean {\r\n if (!expiresAt || !Number.isFinite(expiresAt)) {\r\n return true;\r\n }\r\n return Date.now() + MIN_TOKEN_BUFFER_MS >= expiresAt;\r\n}\r\n\r\nasync function safeReadError(response: Response): Promise<string> {\r\n try {\r\n const body = await response.text();\r\n if (!body) {\r\n return 'No response body';\r\n }\r\n try {\r\n const parsed = JSON.parse(body) as Record<string, unknown>;\r\n return typeof parsed.error === 'string'\r\n ? parsed.error\r\n : typeof parsed.message === 'string'\r\n ? parsed.message\r\n : body;\r\n } catch {\r\n return body;\r\n }\r\n } catch {\r\n return 'No response body';\r\n }\r\n}\r\n\r\nasync function main(): Promise<void> {\r\n const [, , ...argv] = process.argv;\r\n const { command, args } = parseCommandLine(argv);\r\n\r\n if (!command || command === '--help' || command === '-h') {\r\n printHelp();\r\n return;\r\n }\r\n\r\n const credentialStore = await CredentialStore.create();\r\n const tokenManager = new TokenManager(credentialStore);\r\n const configStore = new ConfigStore();\r\n\r\n switch (command) {\r\n case 'login':\r\n await handleLogin(args, configStore, tokenManager);\r\n break;\r\n case 'publish':\r\n await handlePublish(args, configStore, tokenManager);\r\n break;\r\n case 'validate':\r\n await handleValidate(args, configStore, tokenManager);\r\n break;\r\n case 'profiles':\r\n await listProfiles(configStore);\r\n break;\r\n default:\r\n console.error(`Unknown command: ${command}`);\r\n printHelp();\r\n exit(1);\r\n }\r\n}\r\n\r\nasync function listProfiles(configStore: ConfigStore): Promise<void> {\r\n const config = await configStore.load();\r\n const entries = Object.values(config.profiles).sort((a, b) => (b.lastUsed ?? 0) - (a.lastUsed ?? 0));\r\n if (entries.length === 0) {\r\n console.log('No stored profiles. Run \"beamo-node login\" to create one.');\r\n return;\r\n }\r\n console.log('Stored profiles:');\r\n for (const profile of entries) {\r\n const marker = config.lastUsed === profile.id ? '*' : ' ';\r\n console.log(` ${marker} ${profile.id}`);\r\n console.log(` CID: ${profile.cid}`);\r\n console.log(` PID: ${profile.pid}`);\r\n console.log(` Host: ${profile.host}`);\r\n console.log(` Email: ${profile.email}`);\r\n }\r\n}\r\n\r\nfunction printHelp(): void {\r\n console.log(`beamo-node CLI\r\n\r\nUsage:\r\n beamo-node login [--cid CID] [--pid PID] [--host HOST] [--email EMAIL]\r\n beamo-node publish [--profile ID] [--cid CID --pid PID --host HOST] [-- ...publish flags...]\r\n beamo-node validate [--profile ID] [--cid CID --pid PID --host HOST] [-- ...validate flags...]\r\n beamo-node profiles\r\n\r\nCommands:\r\n login Authenticate with Beamable and store credentials securely.\r\n publish Run the publish pipeline using stored credentials (forwards extra flags to the publish script).\r\n validate Generate OpenAPI docs and validate the project (forwards extra flags).\r\n profiles List stored profiles and indicate the default selection.\r\n`);\r\n}\r\n\r\nmain().catch((error) => {\r\n console.error(error instanceof Error ? error.message : error);\r\n exit(1);\r\n});\r\n\r\nasync function loadEnvDefaults(): Promise<ProfileSelectionOptions> {\r\n const envPath = path.resolve('.env');\r\n try {\r\n const content = await fs.readFile(envPath, 'utf8');\r\n const parsed = dotenv.parse(content);\r\n const cid = parsed.BEAMABLE_CID ?? parsed.CID ?? undefined;\r\n const pid = parsed.BEAMABLE_PID ?? parsed.PID ?? undefined;\r\n const host = parsed.BEAMABLE_HOST ?? parsed.HOST ?? undefined;\r\n return {\r\n cid: cid?.trim() || undefined,\r\n pid: pid?.trim() || undefined,\r\n host: host?.trim() || undefined,\r\n };\r\n } catch (error) {\r\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n return {};\r\n }\r\n throw error;\r\n }\r\n}\r\n\r\n\r\n"]}
package/dist/logger.cjs CHANGED
@@ -35,7 +35,97 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createLogger = createLogger;
37
37
  const pino_1 = __importStar(require("pino"));
38
+ const node_stream_1 = require("node:stream");
38
39
  const env_js_1 = require("./env.js");
40
+ function mapPinoLevelToBeamableLevel(level) {
41
+ switch (level) {
42
+ case 10:
43
+ return 'Debug';
44
+ case 20:
45
+ return 'Debug';
46
+ case 30:
47
+ return 'Info';
48
+ case 40:
49
+ return 'Warning';
50
+ case 50:
51
+ return 'Error';
52
+ case 60:
53
+ return 'Fatal';
54
+ default:
55
+ return 'Info';
56
+ }
57
+ }
58
+ function createBeamableLogFormatter() {
59
+ return new node_stream_1.Transform({
60
+ objectMode: false,
61
+ transform(chunk, _encoding, callback) {
62
+ try {
63
+ const line = chunk.toString();
64
+ if (!line.trim()) {
65
+ callback();
66
+ return;
67
+ }
68
+ const pinoLog = JSON.parse(line);
69
+ let timestamp;
70
+ if (typeof pinoLog.time === 'string') {
71
+ timestamp = pinoLog.time;
72
+ }
73
+ else if (typeof pinoLog.time === 'number') {
74
+ timestamp = new Date(pinoLog.time).toISOString();
75
+ }
76
+ else {
77
+ timestamp = new Date().toISOString();
78
+ }
79
+ const level = mapPinoLevelToBeamableLevel(pinoLog.level);
80
+ const messageParts = [];
81
+ if (pinoLog.msg) {
82
+ messageParts.push(pinoLog.msg);
83
+ }
84
+ if (pinoLog.err) {
85
+ const err = pinoLog.err;
86
+ const errMsg = err.message || err.msg || 'Error';
87
+ const errStack = err.stack ? `\n${err.stack}` : '';
88
+ messageParts.push(`${errMsg}${errStack}`);
89
+ }
90
+ const beamableLog = {
91
+ __t: timestamp,
92
+ __l: level,
93
+ __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',
94
+ };
95
+ const contextFields = {};
96
+ if (pinoLog.cid)
97
+ contextFields.cid = pinoLog.cid;
98
+ if (pinoLog.pid)
99
+ contextFields.pid = pinoLog.pid;
100
+ if (pinoLog.routingKey)
101
+ contextFields.routingKey = pinoLog.routingKey;
102
+ if (pinoLog.service)
103
+ contextFields.service = pinoLog.service;
104
+ if (pinoLog.component)
105
+ contextFields.component = pinoLog.component;
106
+ const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];
107
+ for (const [key, value] of Object.entries(pinoLog)) {
108
+ if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {
109
+ contextFields[key] = value;
110
+ }
111
+ }
112
+ if (Object.keys(contextFields).length > 0) {
113
+ beamableLog.__c = contextFields;
114
+ }
115
+ const output = JSON.stringify(beamableLog) + '\n';
116
+ callback(null, Buffer.from(output, 'utf8'));
117
+ }
118
+ catch (error) {
119
+ const fallbackLog = {
120
+ __t: new Date().toISOString(),
121
+ __l: 'Error',
122
+ __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,
123
+ };
124
+ callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\n', 'utf8'));
125
+ }
126
+ },
127
+ });
128
+ }
39
129
  function createLogger(env, options = {}) {
40
130
  var _a, _b, _c;
41
131
  const configuredDestination = (_a = options.destinationPath) !== null && _a !== void 0 ? _a : process.env.LOG_PATH;
@@ -52,11 +142,16 @@ function createLogger(env, options = {}) {
52
142
  paths: ['secret', 'refreshToken'],
53
143
  censor: '***',
54
144
  },
145
+ timestamp: pino_1.default.stdTimeFunctions.isoTime,
55
146
  };
56
147
  if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {
57
- return (0, pino_1.default)(pinoOptions, process.stdout);
148
+ const beamableFormatter = createBeamableLogFormatter();
149
+ beamableFormatter.pipe(process.stdout);
150
+ return (0, pino_1.default)(pinoOptions, beamableFormatter);
58
151
  }
59
152
  const resolvedDestination = configuredDestination === 'temp' ? (0, env_js_1.ensureWritableTempDirectory)() : configuredDestination;
60
- const stream = (0, pino_1.destination)({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
61
- return (0, pino_1.default)(pinoOptions, stream);
153
+ const beamableFormatter = createBeamableLogFormatter();
154
+ const fileStream = (0, pino_1.destination)({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
155
+ beamableFormatter.pipe(fileStream);
156
+ return (0, pino_1.default)(pinoOptions, beamableFormatter);
62
157
  }
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CA4B/F"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAa,EAAe,KAAK,MAAM,EAAsB,MAAM,MAAM,CAAC;AAG1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,UAAU,oBAAoB;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAwHD,wBAAgB,YAAY,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAyC/F"}
package/dist/logger.js CHANGED
@@ -1,5 +1,120 @@
1
1
  import pino, { destination } from 'pino';
2
+ import { Transform } from 'node:stream';
2
3
  import { ensureWritableTempDirectory } from './env.js';
4
+ /**
5
+ * Maps Pino log levels to Beamable log levels
6
+ * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal
7
+ * Beamable levels: Debug, Info, Warning, Error, Fatal
8
+ */
9
+ function mapPinoLevelToBeamableLevel(level) {
10
+ switch (level) {
11
+ case 10: // trace
12
+ return 'Debug';
13
+ case 20: // debug
14
+ return 'Debug';
15
+ case 30: // info
16
+ return 'Info';
17
+ case 40: // warn
18
+ return 'Warning';
19
+ case 50: // error
20
+ return 'Error';
21
+ case 60: // fatal
22
+ return 'Fatal';
23
+ default:
24
+ return 'Info';
25
+ }
26
+ }
27
+ /**
28
+ * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.
29
+ * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.
30
+ * Pino writes JSON strings (one per line) to the stream.
31
+ */
32
+ function createBeamableLogFormatter() {
33
+ return new Transform({
34
+ objectMode: false, // Pino writes strings, not objects
35
+ transform(chunk, _encoding, callback) {
36
+ try {
37
+ const line = chunk.toString();
38
+ // Skip empty lines
39
+ if (!line.trim()) {
40
+ callback();
41
+ return;
42
+ }
43
+ // Parse Pino's JSON log line
44
+ const pinoLog = JSON.parse(line);
45
+ // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)
46
+ // Convert to ISO 8601 string for Beamable
47
+ let timestamp;
48
+ if (typeof pinoLog.time === 'string') {
49
+ timestamp = pinoLog.time;
50
+ }
51
+ else if (typeof pinoLog.time === 'number') {
52
+ timestamp = new Date(pinoLog.time).toISOString();
53
+ }
54
+ else {
55
+ timestamp = new Date().toISOString();
56
+ }
57
+ // Map Pino level to Beamable level
58
+ const level = mapPinoLevelToBeamableLevel(pinoLog.level);
59
+ // Build the message - combine msg with any additional fields
60
+ // Pino's 'msg' field contains the log message
61
+ const messageParts = [];
62
+ if (pinoLog.msg) {
63
+ messageParts.push(pinoLog.msg);
64
+ }
65
+ // Include error information if present
66
+ if (pinoLog.err) {
67
+ const err = pinoLog.err;
68
+ const errMsg = err.message || err.msg || 'Error';
69
+ const errStack = err.stack ? `\n${err.stack}` : '';
70
+ messageParts.push(`${errMsg}${errStack}`);
71
+ }
72
+ // Build the Beamable log format
73
+ const beamableLog = {
74
+ __t: timestamp,
75
+ __l: level,
76
+ __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',
77
+ };
78
+ // Include additional context fields that might be useful
79
+ // These are included in the message object but not as top-level fields
80
+ const contextFields = {};
81
+ if (pinoLog.cid)
82
+ contextFields.cid = pinoLog.cid;
83
+ if (pinoLog.pid)
84
+ contextFields.pid = pinoLog.pid;
85
+ if (pinoLog.routingKey)
86
+ contextFields.routingKey = pinoLog.routingKey;
87
+ if (pinoLog.service)
88
+ contextFields.service = pinoLog.service;
89
+ if (pinoLog.component)
90
+ contextFields.component = pinoLog.component;
91
+ // Include any other fields that aren't standard Pino fields
92
+ const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];
93
+ for (const [key, value] of Object.entries(pinoLog)) {
94
+ if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {
95
+ contextFields[key] = value;
96
+ }
97
+ }
98
+ // If there are context fields, include them in the log
99
+ if (Object.keys(contextFields).length > 0) {
100
+ beamableLog.__c = contextFields;
101
+ }
102
+ // Output as a single-line JSON string (required for CloudWatch)
103
+ const output = JSON.stringify(beamableLog) + '\n';
104
+ callback(null, Buffer.from(output, 'utf8'));
105
+ }
106
+ catch (error) {
107
+ // If parsing fails, output a fallback log entry
108
+ const fallbackLog = {
109
+ __t: new Date().toISOString(),
110
+ __l: 'Error',
111
+ __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,
112
+ };
113
+ callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\n', 'utf8'));
114
+ }
115
+ },
116
+ });
117
+ }
3
118
  export function createLogger(env, options = {}) {
4
119
  const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;
5
120
  const pinoOptions = {
@@ -15,15 +130,26 @@ export function createLogger(env, options = {}) {
15
130
  paths: ['secret', 'refreshToken'],
16
131
  censor: '***',
17
132
  },
133
+ // Use timestamp in milliseconds (Pino default) for accurate conversion
134
+ timestamp: pino.stdTimeFunctions.isoTime,
18
135
  };
19
136
  // For deployed services, always log to stdout so container orchestrator can collect logs
20
137
  // For local development, log to stdout unless a specific file path is provided
21
138
  if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {
22
- // Log to stdout (default pino behavior) - this is critical for container log collection
23
- return pino(pinoOptions, process.stdout);
139
+ // Create a transform stream that formats logs for Beamable
140
+ // Pino outputs JSON to the transform, which converts it to Beamable format
141
+ const beamableFormatter = createBeamableLogFormatter();
142
+ beamableFormatter.pipe(process.stdout);
143
+ // Create Pino logger that writes to the formatter
144
+ return pino(pinoOptions, beamableFormatter);
24
145
  }
146
+ // For file logging, also use Beamable format
25
147
  const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;
26
- const stream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
27
- return pino(pinoOptions, stream);
148
+ const beamableFormatter = createBeamableLogFormatter();
149
+ const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
150
+ // Pipe the formatted output to the file stream
151
+ // Note: destination() returns a SonicBoom stream which is compatible with Node streams
152
+ beamableFormatter.pipe(fileStream);
153
+ return pino(pinoOptions, beamableFormatter);
28
154
  }
29
155
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,EAAE,EAAE,WAAW,EAAmC,MAAM,MAAM,CAAC;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC;AAQvD,MAAM,UAAU,YAAY,CAAC,GAAsB,EAAE,UAAgC,EAAE;IACrF,MAAM,qBAAqB,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAE9E,MAAM,WAAW,GAAkB;QACjC,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,uBAAuB;QAC7C,KAAK,EAAE,GAAG,CAAC,QAAQ;QACnB,IAAI,EAAE;YACJ,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,IAAI;YAClC,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;SAC7C;QACD,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC;YACjC,MAAM,EAAE,KAAK;SACd;KACF,CAAC;IAEF,yFAAyF;IACzF,+EAA+E;IAC/E,IAAI,CAAC,qBAAqB,IAAI,qBAAqB,KAAK,GAAG,IAAI,qBAAqB,KAAK,QAAQ,IAAI,qBAAqB,KAAK,SAAS,EAAE,CAAC;QACzI,wFAAwF;QACxF,OAAO,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,mBAAmB,GAAG,qBAAqB,KAAK,MAAM,CAAC,CAAC,CAAC,2BAA2B,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC;IACrH,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClG,OAAO,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AACnC,CAAC","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n // Log to stdout (default pino behavior) - this is critical for container log collection\r\n return pino(pinoOptions, process.stdout);\r\n }\r\n\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n const stream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n return pino(pinoOptions, stream);\r\n}\r\n"]}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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","sourcesContent":["import pino, { destination, type Logger, type LoggerOptions } from 'pino';\r\nimport { Transform } from 'node:stream';\r\nimport { ensureWritableTempDirectory } from './env.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\n\r\ninterface LoggerFactoryOptions {\r\n name?: string;\r\n destinationPath?: string;\r\n}\r\n\r\n/**\r\n * Maps Pino log levels to Beamable log levels\r\n * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal\r\n * Beamable levels: Debug, Info, Warning, Error, Fatal\r\n */\r\nfunction mapPinoLevelToBeamableLevel(level: number): string {\r\n switch (level) {\r\n case 10: // trace\r\n return 'Debug';\r\n case 20: // debug\r\n return 'Debug';\r\n case 30: // info\r\n return 'Info';\r\n case 40: // warn\r\n return 'Warning';\r\n case 50: // error\r\n return 'Error';\r\n case 60: // fatal\r\n return 'Fatal';\r\n default:\r\n return 'Info';\r\n }\r\n}\r\n\r\n/**\r\n * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.\r\n * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.\r\n * Pino writes JSON strings (one per line) to the stream.\r\n */\r\nfunction createBeamableLogFormatter(): Transform {\r\n return new Transform({\r\n objectMode: false, // Pino writes strings, not objects\r\n transform(chunk: Buffer, _encoding, callback) {\r\n try {\r\n const line = chunk.toString();\r\n // Skip empty lines\r\n if (!line.trim()) {\r\n callback();\r\n return;\r\n }\r\n \r\n // Parse Pino's JSON log line\r\n const pinoLog = JSON.parse(line);\r\n \r\n // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)\r\n // Convert to ISO 8601 string for Beamable\r\n let timestamp: string;\r\n if (typeof pinoLog.time === 'string') {\r\n timestamp = pinoLog.time;\r\n } else if (typeof pinoLog.time === 'number') {\r\n timestamp = new Date(pinoLog.time).toISOString();\r\n } else {\r\n timestamp = new Date().toISOString();\r\n }\r\n \r\n // Map Pino level to Beamable level\r\n const level = mapPinoLevelToBeamableLevel(pinoLog.level);\r\n \r\n // Build the message - combine msg with any additional fields\r\n // Pino's 'msg' field contains the log message\r\n const messageParts: string[] = [];\r\n if (pinoLog.msg) {\r\n messageParts.push(pinoLog.msg);\r\n }\r\n \r\n // Include error information if present\r\n if (pinoLog.err) {\r\n const err = pinoLog.err;\r\n const errMsg = err.message || err.msg || 'Error';\r\n const errStack = err.stack ? `\\n${err.stack}` : '';\r\n messageParts.push(`${errMsg}${errStack}`);\r\n }\r\n \r\n // Build the Beamable log format\r\n const beamableLog: Record<string, unknown> = {\r\n __t: timestamp,\r\n __l: level,\r\n __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',\r\n };\r\n \r\n // Include additional context fields that might be useful\r\n // These are included in the message object but not as top-level fields\r\n const contextFields: Record<string, unknown> = {};\r\n if (pinoLog.cid) contextFields.cid = pinoLog.cid;\r\n if (pinoLog.pid) contextFields.pid = pinoLog.pid;\r\n if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;\r\n if (pinoLog.service) contextFields.service = pinoLog.service;\r\n if (pinoLog.component) contextFields.component = pinoLog.component;\r\n \r\n // Include any other fields that aren't standard Pino fields\r\n const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];\r\n for (const [key, value] of Object.entries(pinoLog)) {\r\n if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {\r\n contextFields[key] = value;\r\n }\r\n }\r\n \r\n // If there are context fields, include them in the log\r\n if (Object.keys(contextFields).length > 0) {\r\n beamableLog.__c = contextFields;\r\n }\r\n \r\n // Output as a single-line JSON string (required for CloudWatch)\r\n const output = JSON.stringify(beamableLog) + '\\n';\r\n callback(null, Buffer.from(output, 'utf8'));\r\n } catch (error) {\r\n // If parsing fails, output a fallback log entry\r\n const fallbackLog = {\r\n __t: new Date().toISOString(),\r\n __l: 'Error',\r\n __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,\r\n };\r\n callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\\n', 'utf8'));\r\n }\r\n },\r\n });\r\n}\r\n\r\nexport function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {\r\n const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;\r\n\r\n const pinoOptions: LoggerOptions = {\r\n name: options.name ?? 'beamable-node-runtime',\r\n level: env.logLevel,\r\n base: {\r\n cid: env.cid,\r\n pid: env.pid,\r\n routingKey: env.routingKey ?? null,\r\n sdkVersionExecution: env.sdkVersionExecution,\r\n },\r\n redact: {\r\n paths: ['secret', 'refreshToken'],\r\n censor: '***',\r\n },\r\n // Use timestamp in milliseconds (Pino default) for accurate conversion\r\n timestamp: pino.stdTimeFunctions.isoTime,\r\n };\r\n\r\n // For deployed services, always log to stdout so container orchestrator can collect logs\r\n // For local development, log to stdout unless a specific file path is provided\r\n if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {\r\n // Create a transform stream that formats logs for Beamable\r\n // Pino outputs JSON to the transform, which converts it to Beamable format\r\n const beamableFormatter = createBeamableLogFormatter();\r\n beamableFormatter.pipe(process.stdout);\r\n \r\n // Create Pino logger that writes to the formatter\r\n return pino(pinoOptions, beamableFormatter);\r\n }\r\n\r\n // For file logging, also use Beamable format\r\n const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;\r\n const beamableFormatter = createBeamableLogFormatter();\r\n const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });\r\n // Pipe the formatted output to the file stream\r\n // Note: destination() returns a SonicBoom stream which is compatible with Node streams\r\n beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);\r\n \r\n return pino(pinoOptions, beamableFormatter);\r\n}\r\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omen.foundation/node-microservice-runtime",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Beamable microservice runtime for Node.js/TypeScript services.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/cli/index.ts CHANGED
@@ -421,6 +421,19 @@ async function handlePublish(args: string[], configStore: ConfigStore, tokenMana
421
421
  const env = buildCommandEnv(profile, tokens);
422
422
 
423
423
  const scriptArgs = buildScriptArgs(forward, profile, true); // publish needs --api-host
424
+
425
+ // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided
426
+ const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));
427
+ if (!hasEnvFileFlag) {
428
+ const envPath = path.resolve('.env');
429
+ try {
430
+ await fs.access(envPath);
431
+ scriptArgs.push('--env-file', '.env');
432
+ } catch {
433
+ // .env file doesn't exist, that's okay
434
+ }
435
+ }
436
+
424
437
  const scriptPath = path.resolve(__dirname, '../../scripts/publish-service.mjs');
425
438
  await runScript(scriptPath, scriptArgs, env);
426
439
  }
@@ -434,6 +447,19 @@ async function handleValidate(args: string[], configStore: ConfigStore, tokenMan
434
447
  const env = buildCommandEnv(profile, tokens);
435
448
 
436
449
  const scriptArgs = buildScriptArgs(forward, profile, false); // validate doesn't need --api-host
450
+
451
+ // Automatically add --env-file .env if .env exists and --env-file wasn't explicitly provided
452
+ const hasEnvFileFlag = forward.some((arg, i) => arg === '--env-file' || (i > 0 && forward[i - 1] === '--env-file'));
453
+ if (!hasEnvFileFlag) {
454
+ const envPath = path.resolve('.env');
455
+ try {
456
+ await fs.access(envPath);
457
+ scriptArgs.push('--env-file', '.env');
458
+ } catch {
459
+ // .env file doesn't exist, that's okay
460
+ }
461
+ }
462
+
437
463
  const scriptPath = path.resolve(__dirname, '../../scripts/validate-service.mjs');
438
464
  await runScript(scriptPath, scriptArgs, env);
439
465
  }
package/src/logger.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import pino, { destination, type Logger, type LoggerOptions } from 'pino';
2
+ import { Transform } from 'node:stream';
2
3
  import { ensureWritableTempDirectory } from './env.js';
3
4
  import type { EnvironmentConfig } from './types.js';
4
5
 
@@ -7,6 +8,124 @@ interface LoggerFactoryOptions {
7
8
  destinationPath?: string;
8
9
  }
9
10
 
11
+ /**
12
+ * Maps Pino log levels to Beamable log levels
13
+ * Pino levels: 10=trace, 20=debug, 30=info, 40=warn, 50=error, 60=fatal
14
+ * Beamable levels: Debug, Info, Warning, Error, Fatal
15
+ */
16
+ function mapPinoLevelToBeamableLevel(level: number): string {
17
+ switch (level) {
18
+ case 10: // trace
19
+ return 'Debug';
20
+ case 20: // debug
21
+ return 'Debug';
22
+ case 30: // info
23
+ return 'Info';
24
+ case 40: // warn
25
+ return 'Warning';
26
+ case 50: // error
27
+ return 'Error';
28
+ case 60: // fatal
29
+ return 'Fatal';
30
+ default:
31
+ return 'Info';
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Creates a transform stream that converts Pino JSON logs to Beamable's expected format.
37
+ * Beamable expects logs with __t (timestamp), __l (level), and __m (message) fields.
38
+ * Pino writes JSON strings (one per line) to the stream.
39
+ */
40
+ function createBeamableLogFormatter(): Transform {
41
+ return new Transform({
42
+ objectMode: false, // Pino writes strings, not objects
43
+ transform(chunk: Buffer, _encoding, callback) {
44
+ try {
45
+ const line = chunk.toString();
46
+ // Skip empty lines
47
+ if (!line.trim()) {
48
+ callback();
49
+ return;
50
+ }
51
+
52
+ // Parse Pino's JSON log line
53
+ const pinoLog = JSON.parse(line);
54
+
55
+ // Extract timestamp - Pino uses 'time' field (ISO 8601 string or milliseconds)
56
+ // Convert to ISO 8601 string for Beamable
57
+ let timestamp: string;
58
+ if (typeof pinoLog.time === 'string') {
59
+ timestamp = pinoLog.time;
60
+ } else if (typeof pinoLog.time === 'number') {
61
+ timestamp = new Date(pinoLog.time).toISOString();
62
+ } else {
63
+ timestamp = new Date().toISOString();
64
+ }
65
+
66
+ // Map Pino level to Beamable level
67
+ const level = mapPinoLevelToBeamableLevel(pinoLog.level);
68
+
69
+ // Build the message - combine msg with any additional fields
70
+ // Pino's 'msg' field contains the log message
71
+ const messageParts: string[] = [];
72
+ if (pinoLog.msg) {
73
+ messageParts.push(pinoLog.msg);
74
+ }
75
+
76
+ // Include error information if present
77
+ if (pinoLog.err) {
78
+ const err = pinoLog.err;
79
+ const errMsg = err.message || err.msg || 'Error';
80
+ const errStack = err.stack ? `\n${err.stack}` : '';
81
+ messageParts.push(`${errMsg}${errStack}`);
82
+ }
83
+
84
+ // Build the Beamable log format
85
+ const beamableLog: Record<string, unknown> = {
86
+ __t: timestamp,
87
+ __l: level,
88
+ __m: messageParts.length > 0 ? messageParts.join(' ') : 'No message',
89
+ };
90
+
91
+ // Include additional context fields that might be useful
92
+ // These are included in the message object but not as top-level fields
93
+ const contextFields: Record<string, unknown> = {};
94
+ if (pinoLog.cid) contextFields.cid = pinoLog.cid;
95
+ if (pinoLog.pid) contextFields.pid = pinoLog.pid;
96
+ if (pinoLog.routingKey) contextFields.routingKey = pinoLog.routingKey;
97
+ if (pinoLog.service) contextFields.service = pinoLog.service;
98
+ if (pinoLog.component) contextFields.component = pinoLog.component;
99
+
100
+ // Include any other fields that aren't standard Pino fields
101
+ const standardPinoFields = ['level', 'time', 'pid', 'hostname', 'name', 'msg', 'err', 'v', 'cid', 'pid', 'routingKey', 'sdkVersionExecution', 'service', 'component'];
102
+ for (const [key, value] of Object.entries(pinoLog)) {
103
+ if (!standardPinoFields.includes(key) && value !== undefined && value !== null) {
104
+ contextFields[key] = value;
105
+ }
106
+ }
107
+
108
+ // If there are context fields, include them in the log
109
+ if (Object.keys(contextFields).length > 0) {
110
+ beamableLog.__c = contextFields;
111
+ }
112
+
113
+ // Output as a single-line JSON string (required for CloudWatch)
114
+ const output = JSON.stringify(beamableLog) + '\n';
115
+ callback(null, Buffer.from(output, 'utf8'));
116
+ } catch (error) {
117
+ // If parsing fails, output a fallback log entry
118
+ const fallbackLog = {
119
+ __t: new Date().toISOString(),
120
+ __l: 'Error',
121
+ __m: `Failed to parse log entry: ${chunk.toString().substring(0, 200)}`,
122
+ };
123
+ callback(null, Buffer.from(JSON.stringify(fallbackLog) + '\n', 'utf8'));
124
+ }
125
+ },
126
+ });
127
+ }
128
+
10
129
  export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptions = {}): Logger {
11
130
  const configuredDestination = options.destinationPath ?? process.env.LOG_PATH;
12
131
 
@@ -23,16 +142,29 @@ export function createLogger(env: EnvironmentConfig, options: LoggerFactoryOptio
23
142
  paths: ['secret', 'refreshToken'],
24
143
  censor: '***',
25
144
  },
145
+ // Use timestamp in milliseconds (Pino default) for accurate conversion
146
+ timestamp: pino.stdTimeFunctions.isoTime,
26
147
  };
27
148
 
28
149
  // For deployed services, always log to stdout so container orchestrator can collect logs
29
150
  // For local development, log to stdout unless a specific file path is provided
30
151
  if (!configuredDestination || configuredDestination === '-' || configuredDestination === 'stdout' || configuredDestination === 'console') {
31
- // Log to stdout (default pino behavior) - this is critical for container log collection
32
- return pino(pinoOptions, process.stdout);
152
+ // Create a transform stream that formats logs for Beamable
153
+ // Pino outputs JSON to the transform, which converts it to Beamable format
154
+ const beamableFormatter = createBeamableLogFormatter();
155
+ beamableFormatter.pipe(process.stdout);
156
+
157
+ // Create Pino logger that writes to the formatter
158
+ return pino(pinoOptions, beamableFormatter);
33
159
  }
34
160
 
161
+ // For file logging, also use Beamable format
35
162
  const resolvedDestination = configuredDestination === 'temp' ? ensureWritableTempDirectory() : configuredDestination;
36
- const stream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
37
- return pino(pinoOptions, stream);
163
+ const beamableFormatter = createBeamableLogFormatter();
164
+ const fileStream = destination({ dest: resolvedDestination, mkdir: true, append: true, sync: false });
165
+ // Pipe the formatted output to the file stream
166
+ // Note: destination() returns a SonicBoom stream which is compatible with Node streams
167
+ beamableFormatter.pipe(fileStream as unknown as NodeJS.WritableStream);
168
+
169
+ return pino(pinoOptions, beamableFormatter);
38
170
  }