Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"@octokit/request-error": "catalog:",
"@octokit/rest": "catalog:",
"@socketaddon/iocraft": "file:../package-builder/build/dev/out/socketaddon-iocraft",
"@socketaddon/opentui": "file:../package-builder/build/dev/out/socketaddon-opentui",
"@socketregistry/hyrious__bun.lockb": "catalog:",
"@socketregistry/indent-string": "catalog:",
"@socketregistry/is-interactive": "catalog:",
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/scripts/download-assets.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ const ASSETS = {
name: 'iocraft',
type: 'multi-platform',
},
opentui: {
description: 'opentui native bindings (.node files)',
download: {
asset: 'opentui-*.node',
cwd: rootPath,
downloadDir: '../../packages/build-infra/build/downloaded/opentui',
quiet: false,
tool: 'opentui',
},
name: 'opentui',
type: 'multi-platform',
},
models: {
description: 'AI models (MiniLM-L6-v2, CodeT5)',
download: {
Expand Down
13 changes: 10 additions & 3 deletions packages/cli/src/utils/terminal/iocraft.mts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,20 @@ function getIocraft(): typeof iocraft {
try {
// Use createRequire to load native .node module from ESM.
const require = createRequire(import.meta.url)
const loaded = require('@socketaddon/iocraft')
// Try opentui first (yoga-layout + opentui.node renderer),
// fall back to iocraft if not available.
let loaded
try {
loaded = require('@socketaddon/opentui')
} catch {
loaded = require('@socketaddon/iocraft')
}
// Handle ESM default export when loaded via require().
iocraftInstance = loaded.default || loaded
} catch (e) {
throw new Error(
`Failed to load iocraft native module: ${e}\n` +
`Make sure @socketaddon/iocraft is installed and your platform is supported.`,
`Failed to load terminal UI module: ${e}\n` +
`Make sure @socketaddon/opentui or @socketaddon/iocraft is installed and your platform is supported.`,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,38 @@
exports[`AnalyticsRenderer > data rendering > should render analytics with fixture data 1`] = `
"Socket Analytics

┌─────────────────────────┐
│ │
│ Top 5 Alert Types: │
│ │
│ envVars: 2533 │
│ unmaintained: 532 │
│ filesystemAccess: 514 │
│ networkAccess: 434 │
│ dynamicRequire: 274 │
│ │
└─────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────
│ Top 5 Alert Types:
│ envVars: 2533
│ unmaintained: 532
│ filesystemAccess: 514
│ networkAccess: 434
│ dynamicRequire: 274
└──────────────────────────────────────────────────────────────────────────────

┌─────────────────────────┐
│ │
│ Critical Alerts │
│ │
│ Apr 19: 0 │
│ Apr 21: 0 │
│ Apr 20: 0 │
│ Apr 22: 0 │
│ │
└─────────────────────────┘

┌─────────────────────────┐
│ │
│ High Alerts │
│ │
│ Apr 19: 13 │
│ Apr 21: 13 │
│ Apr 20: 13 │
│ Apr 22: 10 │
│ │
└─────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ Critical Alerts │
│ │
│ Apr 19: 0 │
│ Apr 21: 0 │
│ Apr 20: 0 │
│ Apr 22: 0 │
│ │
└──────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ High Alerts │
│ │
│ Apr 19: 13 │
│ Apr 21: 13 │
│ Apr 20: 13 │
│ Apr 22: 10 │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
exports[`AuditLogRenderer > data rendering > should render audit log table with multiple entries 1`] = `
"Socket Audit Logs for test-org

┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Event ID Created At Event Type User Email │
│ │
│ evt_12345678901234 Apr 19, 2024 10:30 AM repository.created user@example.com │
│ evt_23456789012345 Apr 19, 2024 11:00 AM settings.updated admin@example.com │
│ │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ Event ID Created At Event Type │
│ │
│ evt_12345678901234 Apr 19, 2024 10:30 AM repository.created │
│ evt_23456789012345 Apr 19, 2024 11:00 AM settings.updated │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
"
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
exports[`ThreatFeedRenderer > data rendering > should render threat feed table with multiple threats 1`] = `
"Socket Threat Feed
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Ecosystem Name Version Type Detected │
│ │
│ npm malicious-pkg 1.0.0 malware test-date │
│ npm suspicious-lib 2.1.0 obfuscation test-date │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────────────┐
│ │
│ Ecosystem Name Version Type │
│ │
│ npm malicious-pkg 1.0.0 malware │
│ npm suspicious-lib 2.1.0 obfuscation │
│ │
└──────────────────────────────────────────────────────────────────────────────┘
"
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* OpenTUI render engine — iocraft-compatible API.
*/

export interface ComponentNode {
type: 'Text' | 'View' | 'MixedText' | 'Fragment'
children?: ComponentNode[]
content?: string
[key: string]: unknown
}

export interface OpenTuiEngine {
renderToString(element: ComponentNode): string
renderToStringWithWidth(element: ComponentNode, maxWidth: number): string
printComponent(element: ComponentNode): void
eprintComponent(element: ComponentNode): void
getTerminalSize(): [number, number]
}

declare const engine: OpenTuiEngine
export default engine
129 changes: 129 additions & 0 deletions packages/package-builder/templates/socketaddon-opentui-main/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/**
* @socketaddon/opentui - Node.js bindings for OpenTUI terminal UI library
*
* Platform detection, native addon loading, and high-level render engine.
* Automatically loads the correct .node binary for the current platform
* and provides iocraft-compatible renderToString/printComponent/eprintComponent API.
*/

import { createRequire } from 'node:module'
import { platform, arch } from 'node:os'

const require = createRequire(import.meta.url)

/**
* Detect the current platform and architecture.
* @returns {string} Platform identifier (e.g., 'darwin-arm64', 'linux-x64-musl')
*/
function getPlatformIdentifier() {
const platformName = platform()
const archName = arch()

const platformMap = {
__proto__: null,
darwin: 'darwin',
linux: 'linux',
win32: 'win',
}

const archMap = {
__proto__: null,
arm64: 'arm64',
x64: 'x64',
}

const mappedPlatform = platformMap[platformName]
const mappedArch = archMap[archName]

if (!mappedPlatform || !mappedArch) {
throw new Error(
`Unsupported platform: ${platformName} ${archName}\n` +
`opentui native bindings are only available for:\n` +
` - macOS (darwin): arm64, x64\n` +
` - Linux (linux): arm64, x64 (glibc and musl)\n` +
` - Windows (win32): arm64, x64`,
)
}

// Detect musl on Linux.
let libcSuffix = ''
if (platformName === 'linux') {
try {
const { spawnSync } = require('node:child_process')
const lddResult = spawnSync('ldd', ['--version'], {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
})
const output = lddResult.stdout || ''
if (output.includes('musl')) {
libcSuffix = '-musl'
}
} catch {
// If ldd fails, assume glibc.
}
}

return `${mappedPlatform}-${mappedArch}${libcSuffix}`
}

/**
* Load the native addon for the current platform.
* @returns {object} The loaded opentui native module
*/
function loadNativeAddon() {
const platformId = getPlatformIdentifier()
const packageName = `@socketaddon/opentui-${platformId}`

try {
// Try to load from optionalDependencies first.
return require(packageName)
} catch (e) {
// Fallback for development: resolve based on actual package location.
try {
const { dirname, join } = require('node:path')
const { fileURLToPath } = require('node:url')
const { realpathSync, existsSync } = require('node:fs')

const __dirname = dirname(fileURLToPath(import.meta.url))
const realDir = realpathSync(__dirname)

let buildOutDir

if (realDir.includes('/build/') && realDir.includes('/out/socketaddon-opentui')) {
buildOutDir = realDir.split('/socketaddon-opentui')[0]
} else if (realDir.includes('@socketaddon+opentui@file+packages+package-builder+build+dev+out+socketaddon-opentui')) {
const match = realDir.match(/^(.+?)\/node_modules\/\.pnpm\/@socketaddon/)
if (match) {
const projectRoot = match[1]
buildOutDir = join(projectRoot, 'packages/package-builder/build/dev/out')
}
}

if (buildOutDir) {
const siblingPath = join(buildOutDir, `socketaddon-opentui-${platformId}`, 'opentui.node')
if (existsSync(siblingPath)) {
return require(siblingPath)
}
}

throw new Error('Not in development build structure')
} catch {
if (e.code === 'MODULE_NOT_FOUND') {
throw new Error(
`Failed to load opentui native addon for ${platformId}.\n` +
`The package ${packageName} is not installed.\n` +
`This usually means your platform is not supported or the optionalDependencies were not installed correctly.\n\n` +
`Try reinstalling with: npm install --force @socketaddon/opentui`,
)
}
throw e
}
}
}

// Load native addon and create render engine.
const native = loadNativeAddon()
const { createRenderEngine } = require('./render-engine.mjs')
const engine = createRenderEngine(native)

export default engine
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "@socketaddon/opentui",
"version": "1.0.0-pre.0",
"description": "Node.js bindings for OpenTUI - a Zig-based terminal UI library with flexbox layout",
"license": "MIT",
"type": "module",
"main": "./index.mjs",
"types": "./index.d.ts",
"exports": {
".": {
"types": "./index.d.ts",
"default": "./index.mjs"
},
"./react": {
"default": "./react.mjs"
}
},
"files": [
"LICENSE",
"README.md",
"index.d.ts",
"index.mjs",
"react.mjs",
"render-engine.mjs",
"yoga-sync.cjs"
],
"optionalDependencies": {
"@socketaddon/opentui-darwin-arm64": "1.0.0-pre.0",
"@socketaddon/opentui-darwin-x64": "1.0.0-pre.0",
"@socketaddon/opentui-linux-arm64": "1.0.0-pre.0",
"@socketaddon/opentui-linux-arm64-musl": "1.0.0-pre.0",
"@socketaddon/opentui-linux-x64": "1.0.0-pre.0",
"@socketaddon/opentui-linux-x64-musl": "1.0.0-pre.0",
"@socketaddon/opentui-win-arm64": "1.0.0-pre.0",
"@socketaddon/opentui-win-x64": "1.0.0-pre.0"
},
"engines": {
"node": ">=18"
},
"repository": {
"type": "git",
"url": "git+https://github.com/SocketDev/socket-cli.git"
},
"author": {
"name": "Socket Inc",
"email": "eng@socket.dev",
"url": "https://socket.dev"
},
"homepage": "https://github.com/SocketDev/socket-cli",
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
}
}
Loading
Loading