omnara 0.5.2 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +804 -332
- package/dist/cli.js.map +4 -4
- package/dist/daemon/machine-supervisor.d.ts +2 -0
- package/dist/daemon/session-runner.d.ts +10 -0
- package/dist/orchestrator/ai-client.d.ts +1 -0
- package/dist/orchestrator/orchestrator-prompt.d.ts +1 -1
- package/dist/orchestrator/session.d.ts +1 -0
- package/dist/package-info.d.ts +9 -0
- package/dist/utils/claude-code-check.d.ts +13 -0
- package/dist/utils/executable.d.ts +53 -0
- package/dist/utils/version-check.d.ts +40 -2
- package/dist/utils/websocket.d.ts +5 -0
- package/package.json +5 -2
- package/scripts/build-executable.ts +244 -0
- package/scripts/generate-release-json.ts +136 -0
- package/scripts/upload-release.ts +388 -0
|
@@ -42,6 +42,8 @@ export declare class MachineSupervisor {
|
|
|
42
42
|
private runWebsocket;
|
|
43
43
|
private handleSocketMessage;
|
|
44
44
|
private handleRpcRequest;
|
|
45
|
+
private handleStartSession;
|
|
46
|
+
private handleStopSession;
|
|
45
47
|
private sendRpcResponse;
|
|
46
48
|
private launchWorkspaceSessionFromRpc;
|
|
47
49
|
private launchWorkspaceSession;
|
|
@@ -25,4 +25,14 @@ export declare class SessionManager {
|
|
|
25
25
|
private readonly logger;
|
|
26
26
|
constructor(options: SessionManagerOptions);
|
|
27
27
|
startSession(spec: SessionLaunchSpec): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Stop a running session by user session ID.
|
|
30
|
+
*
|
|
31
|
+
* Looks up the session in the PID registry, sends SIGTERM for graceful
|
|
32
|
+
* shutdown, waits briefly, then sends SIGKILL if still alive.
|
|
33
|
+
*
|
|
34
|
+
* @returns true if a running session was found and terminated, false if
|
|
35
|
+
* the session was not running.
|
|
36
|
+
*/
|
|
37
|
+
stopSession(userSessionId: string): Promise<boolean>;
|
|
28
38
|
}
|
|
@@ -18,6 +18,7 @@ export declare const BASE_ALLOWED_WORKER_TOOLS: readonly ["Task", "Bash", "Glob"
|
|
|
18
18
|
export declare class AIClient implements AgentClient {
|
|
19
19
|
private baseOptions;
|
|
20
20
|
private query?;
|
|
21
|
+
private abortController?;
|
|
21
22
|
private currentModel;
|
|
22
23
|
constructor(config?: AIClientConfig);
|
|
23
24
|
streamUserMessage(prompt: string | AsyncIterable<SDKUserMessage>, overrides?: Partial<Options>): AsyncIterable<SDKMessage>;
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* The orchestrator's role is to coordinate and delegate work, not to execute tasks directly.
|
|
5
5
|
* It should ALWAYS use workers to handle all implementation work.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
import ORCHESTRATOR_SYSTEM_PROMPT from './orchestrator-prompt.md';
|
|
8
8
|
export { ORCHESTRATOR_SYSTEM_PROMPT };
|
package/dist/package-info.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package information constants.
|
|
3
|
+
*
|
|
4
|
+
* These are replaced at build time by esbuild/bun with values from package.json.
|
|
5
|
+
* The build process uses --define flags to replace __PACKAGE_* constants.
|
|
6
|
+
*
|
|
7
|
+
* In bundled context: Constants are replaced with literal strings at compile time.
|
|
8
|
+
* In dev context: Falls back to default values (actual version comes from build).
|
|
9
|
+
*/
|
|
1
10
|
export declare const PACKAGE_NAME: string;
|
|
2
11
|
export declare const PACKAGE_VERSION: string;
|
|
3
12
|
export declare const PACKAGE_DESCRIPTION: string;
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds the path to the Claude Code executable.
|
|
3
|
+
*
|
|
4
|
+
* When running from a Bun single-file executable, the SDK's default resolution
|
|
5
|
+
* fails because it looks for cli.js relative to the bundled module location,
|
|
6
|
+
* which points to Bun's virtual filesystem (/$bunfs/root/cli.js).
|
|
7
|
+
*
|
|
8
|
+
* For Bun SFE, we MUST find the real claude executable via `which`.
|
|
9
|
+
* For Node.js, we can let the SDK handle it (return empty object).
|
|
10
|
+
*/
|
|
11
|
+
export declare function claudeExecutablePath(): {
|
|
12
|
+
pathToClaudeCodeExecutable?: string;
|
|
13
|
+
};
|
|
1
14
|
/**
|
|
2
15
|
* Performs the Claude Code check and displays warning if needed
|
|
3
16
|
* @param options - Optional configuration for the check
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for handling Bun single-file executable (SFE) detection and spawning.
|
|
3
|
+
*
|
|
4
|
+
* BACKGROUND (from Bun docs and GitHub issues):
|
|
5
|
+
* When Bun compiles a script into a single-file executable, it creates a virtual
|
|
6
|
+
* filesystem at /$bunfs/root/ where the embedded code lives. This means:
|
|
7
|
+
*
|
|
8
|
+
* When running as a Bun SFE:
|
|
9
|
+
* - process.execPath = /path/to/omnara (the compiled binary)
|
|
10
|
+
* - process.argv[0] = "bun"
|
|
11
|
+
* - process.argv[1] = "/$bunfs/root/omnara-darwin-arm64" (virtual filesystem path)
|
|
12
|
+
*
|
|
13
|
+
* When running via Node.js or `bun run`:
|
|
14
|
+
* - process.execPath = /usr/bin/node or /path/to/bun
|
|
15
|
+
* - process.argv[0] = same as execPath
|
|
16
|
+
* - process.argv[1] = /path/to/cli.js (the actual script file)
|
|
17
|
+
*
|
|
18
|
+
* KEY INSIGHT (from Jarred Sumner, Bun team):
|
|
19
|
+
* - For the executable path, use `process.execPath`
|
|
20
|
+
* - The /$bunfs/ path is NOT a usable filesystem location
|
|
21
|
+
*
|
|
22
|
+
* DETECTION:
|
|
23
|
+
* Check if process.argv[1] starts with "/$bunfs/" to detect SFE mode.
|
|
24
|
+
*
|
|
25
|
+
* SPAWNING:
|
|
26
|
+
* In SFE mode: spawn(process.execPath, ['daemon', 'run-service'])
|
|
27
|
+
* In script mode: spawn(process.execPath, [process.argv[1], 'daemon', 'run-service'])
|
|
28
|
+
*
|
|
29
|
+
* References:
|
|
30
|
+
* - https://bun.com/docs/bundler/executables
|
|
31
|
+
* - https://github.com/oven-sh/bun/issues/8476
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Detects if running as a Bun single-file executable.
|
|
35
|
+
* In Bun SFE, process.argv[1] points to Bun's virtual filesystem (/$bunfs/root/...).
|
|
36
|
+
*
|
|
37
|
+
* This is the official detection method per Bun documentation and GitHub issues.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isBunSingleFileExecutable(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Gets the arguments needed to spawn a new instance of this CLI.
|
|
42
|
+
* For Bun SFE, returns just the command args.
|
|
43
|
+
* For Node.js, prepends the script path.
|
|
44
|
+
*
|
|
45
|
+
* @param commandArgs - The CLI command and arguments (e.g., ['daemon', 'run-service'])
|
|
46
|
+
* @returns The full args array to pass to spawn(process.execPath, args)
|
|
47
|
+
*/
|
|
48
|
+
export declare function getSpawnArgs(commandArgs: string[]): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Gets the executable path for spawning a new instance of this CLI.
|
|
51
|
+
* Always returns process.execPath (works for both Node.js and Bun SFE).
|
|
52
|
+
*/
|
|
53
|
+
export declare function getExecutablePath(): string;
|
|
@@ -1,5 +1,43 @@
|
|
|
1
|
+
export declare const IS_PRERELEASE: boolean;
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
-
|
|
3
|
+
* Detects if the CLI was installed via npm (not as a standalone binary).
|
|
4
|
+
*/
|
|
5
|
+
export declare function isNpmInstallation(): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Runs the installation script to install/update the binary version.
|
|
8
|
+
* Returns true if installation succeeded.
|
|
9
|
+
* Passes PRERELEASE=1 env var to install script if in prerelease mode.
|
|
10
|
+
*
|
|
11
|
+
* @param options.silent - If true, suppress output (for daemon background updates)
|
|
12
|
+
*/
|
|
13
|
+
export declare function runInstallScript(options?: {
|
|
14
|
+
silent?: boolean;
|
|
15
|
+
}): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Checks if an update is available.
|
|
18
|
+
* Checks prerelease channel if PRERELEASE=1 env var is set.
|
|
19
|
+
*/
|
|
20
|
+
export declare function checkForUpdate(): Promise<{
|
|
21
|
+
available: boolean;
|
|
22
|
+
latestVersion?: string;
|
|
23
|
+
currentVersion: string;
|
|
24
|
+
isPrerelease: boolean;
|
|
25
|
+
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Performs an update by running the install script.
|
|
28
|
+
* Used by both CLI and daemon for updates.
|
|
29
|
+
*/
|
|
30
|
+
export declare function performUpdate(options?: {
|
|
31
|
+
silent?: boolean;
|
|
32
|
+
}): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Background update check for the daemon.
|
|
35
|
+
* Checks for updates and triggers install script if available.
|
|
36
|
+
*/
|
|
37
|
+
export declare function daemonUpdateCheck(): Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Checks for updates and performs auto-update if available.
|
|
40
|
+
* Called when CLI starts.
|
|
41
|
+
* Returns true if an update was installed and the CLI should exit.
|
|
4
42
|
*/
|
|
5
43
|
export declare function attemptUpdate(): Promise<boolean>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnara",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Command-line interface for Omnara.",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "npm run build:types && node build.mjs && npm run copy-assets",
|
|
13
13
|
"build:types": "tsc --emitDeclarationOnly --project tsconfig.json",
|
|
14
|
+
"build:exe": "bun run scripts/build-executable.ts",
|
|
15
|
+
"build:exe:all": "bun run scripts/build-executable.ts --all",
|
|
16
|
+
"build:release": "bun run scripts/build-executable.ts --all && bun run scripts/generate-release-json.ts",
|
|
14
17
|
"copy-assets": "node scripts/copy-assets.mjs",
|
|
15
18
|
"preinstall": "node scripts/preinstall.js"
|
|
16
19
|
},
|
|
@@ -31,7 +34,7 @@
|
|
|
31
34
|
"@anthropic-ai/claude-agent-sdk": "^0.1.42",
|
|
32
35
|
"@anthropic-ai/sdk": "^0.67.0",
|
|
33
36
|
"@modelcontextprotocol/sdk": "^1.20.1",
|
|
34
|
-
"@sentry/node": "^10.
|
|
37
|
+
"@sentry/node": "^10.28.0",
|
|
35
38
|
"@types/glob": "^8.1.0",
|
|
36
39
|
"ajv": "^8.17.1",
|
|
37
40
|
"ajv-formats": "^2.1.1",
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Build script for creating Bun single-file executables.
|
|
4
|
+
*
|
|
5
|
+
* This script uses Bun's built-in compile feature to create standalone
|
|
6
|
+
* executables that include the Bun runtime and all dependencies.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun run scripts/build-executable.ts # Build for current platform
|
|
10
|
+
* bun run scripts/build-executable.ts --all # Build for all platforms
|
|
11
|
+
* bun run scripts/build-executable.ts --target linux-x64 # Build for specific target
|
|
12
|
+
*
|
|
13
|
+
* Supported targets:
|
|
14
|
+
* - darwin-arm64 (macOS Apple Silicon)
|
|
15
|
+
* - darwin-x64 (macOS Intel)
|
|
16
|
+
* - linux-x64 (Linux x64)
|
|
17
|
+
* - linux-arm64 (Linux ARM64)
|
|
18
|
+
* - windows-x64 (Windows x64)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { spawn } from 'bun';
|
|
22
|
+
import { existsSync, mkdirSync, statSync } from 'node:fs';
|
|
23
|
+
import { dirname, join, resolve } from 'node:path';
|
|
24
|
+
import { readFileSync } from 'node:fs';
|
|
25
|
+
|
|
26
|
+
// Get the path to the bun executable (handles cases where bun isn't in PATH)
|
|
27
|
+
const bunPath = process.execPath;
|
|
28
|
+
|
|
29
|
+
const projectRoot = resolve(dirname(import.meta.path), '..');
|
|
30
|
+
const distDir = join(projectRoot, 'dist');
|
|
31
|
+
const exeDir = join(distDir, 'exe');
|
|
32
|
+
const entryPoint = join(projectRoot, 'src', 'cli.ts');
|
|
33
|
+
|
|
34
|
+
// Read package.json for version info
|
|
35
|
+
const pkgJson = JSON.parse(
|
|
36
|
+
readFileSync(join(projectRoot, 'package.json'), 'utf8')
|
|
37
|
+
) as { version?: string; name?: string; description?: string };
|
|
38
|
+
|
|
39
|
+
const version = pkgJson.version ?? '0.0.0';
|
|
40
|
+
const packageName = pkgJson.name ?? 'omnara';
|
|
41
|
+
const packageDescription = pkgJson.description ?? 'Command-line interface for Omnara.';
|
|
42
|
+
|
|
43
|
+
// Bun compile targets
|
|
44
|
+
type BunTarget =
|
|
45
|
+
| 'bun-darwin-arm64'
|
|
46
|
+
| 'bun-darwin-x64'
|
|
47
|
+
| 'bun-linux-x64'
|
|
48
|
+
| 'bun-linux-arm64'
|
|
49
|
+
| 'bun-linux-x64-baseline'
|
|
50
|
+
| 'bun-linux-arm64-musl'
|
|
51
|
+
| 'bun-linux-x64-musl'
|
|
52
|
+
| 'bun-windows-x64'
|
|
53
|
+
| 'bun-windows-x64-baseline';
|
|
54
|
+
|
|
55
|
+
interface TargetConfig {
|
|
56
|
+
bunTarget: BunTarget;
|
|
57
|
+
outputName: string;
|
|
58
|
+
description: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const TARGETS: Record<string, TargetConfig> = {
|
|
62
|
+
'darwin-arm64': {
|
|
63
|
+
bunTarget: 'bun-darwin-arm64',
|
|
64
|
+
outputName: 'omnara-darwin-arm64',
|
|
65
|
+
description: 'macOS Apple Silicon',
|
|
66
|
+
},
|
|
67
|
+
'darwin-x64': {
|
|
68
|
+
bunTarget: 'bun-darwin-x64',
|
|
69
|
+
outputName: 'omnara-darwin-x64',
|
|
70
|
+
description: 'macOS Intel',
|
|
71
|
+
},
|
|
72
|
+
'linux-x64': {
|
|
73
|
+
bunTarget: 'bun-linux-x64',
|
|
74
|
+
outputName: 'omnara-linux-x64',
|
|
75
|
+
description: 'Linux x64',
|
|
76
|
+
},
|
|
77
|
+
'linux-arm64': {
|
|
78
|
+
bunTarget: 'bun-linux-arm64',
|
|
79
|
+
outputName: 'omnara-linux-arm64',
|
|
80
|
+
description: 'Linux ARM64',
|
|
81
|
+
},
|
|
82
|
+
'linux-x64-musl': {
|
|
83
|
+
bunTarget: 'bun-linux-x64-musl',
|
|
84
|
+
outputName: 'omnara-linux-x64-musl',
|
|
85
|
+
description: 'Linux x64 (musl/Alpine)',
|
|
86
|
+
},
|
|
87
|
+
'windows-x64': {
|
|
88
|
+
bunTarget: 'bun-windows-x64',
|
|
89
|
+
outputName: 'omnara-windows-x64.exe',
|
|
90
|
+
description: 'Windows x64',
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Map current platform to target name
|
|
95
|
+
function getCurrentPlatformTarget(): string {
|
|
96
|
+
const platform = process.platform;
|
|
97
|
+
const arch = process.arch;
|
|
98
|
+
|
|
99
|
+
if (platform === 'darwin' && arch === 'arm64') return 'darwin-arm64';
|
|
100
|
+
if (platform === 'darwin' && arch === 'x64') return 'darwin-x64';
|
|
101
|
+
if (platform === 'linux' && arch === 'x64') return 'linux-x64';
|
|
102
|
+
if (platform === 'linux' && arch === 'arm64') return 'linux-arm64';
|
|
103
|
+
if (platform === 'win32' && arch === 'x64') return 'windows-x64';
|
|
104
|
+
|
|
105
|
+
throw new Error(`Unsupported platform: ${platform}-${arch}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function buildTarget(targetName: string, config: TargetConfig): Promise<boolean> {
|
|
109
|
+
const outputPath = join(exeDir, config.outputName);
|
|
110
|
+
|
|
111
|
+
console.log(`\nBuilding ${config.description} (${targetName})...`);
|
|
112
|
+
console.log(` Target: ${config.bunTarget}`);
|
|
113
|
+
console.log(` Output: ${outputPath}`);
|
|
114
|
+
console.log(` Version: ${version}`);
|
|
115
|
+
|
|
116
|
+
const args = [
|
|
117
|
+
'build',
|
|
118
|
+
entryPoint,
|
|
119
|
+
'--compile',
|
|
120
|
+
'--target', config.bunTarget,
|
|
121
|
+
'--outfile', outputPath,
|
|
122
|
+
'--minify',
|
|
123
|
+
'--sourcemap',
|
|
124
|
+
// Define package info constants at compile time (values must be JSON strings)
|
|
125
|
+
'--define', `__PACKAGE_NAME__=${JSON.stringify(packageName)}`,
|
|
126
|
+
'--define', `__PACKAGE_VERSION__=${JSON.stringify(version)}`,
|
|
127
|
+
'--define', `__PACKAGE_DESCRIPTION__=${JSON.stringify(packageDescription)}`,
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
// Debug: print full command
|
|
131
|
+
console.log(` Command: ${bunPath} ${args.join(' ')}`);
|
|
132
|
+
|
|
133
|
+
const proc = spawn({
|
|
134
|
+
cmd: [bunPath, ...args],
|
|
135
|
+
cwd: projectRoot,
|
|
136
|
+
stdout: 'inherit',
|
|
137
|
+
stderr: 'inherit',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const exitCode = await proc.exited;
|
|
141
|
+
|
|
142
|
+
if (exitCode !== 0) {
|
|
143
|
+
console.error(` Build failed with exit code ${exitCode}`);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Get file size
|
|
148
|
+
if (existsSync(outputPath)) {
|
|
149
|
+
const stats = statSync(outputPath);
|
|
150
|
+
const sizeMB = (stats.size / (1024 * 1024)).toFixed(2);
|
|
151
|
+
console.log(` Size: ${sizeMB} MB`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function main() {
|
|
158
|
+
const args = process.argv.slice(2);
|
|
159
|
+
|
|
160
|
+
console.log('='.repeat(60));
|
|
161
|
+
console.log(`Omnara CLI Executable Builder (v${version})`);
|
|
162
|
+
console.log('='.repeat(60));
|
|
163
|
+
|
|
164
|
+
// Create output directory
|
|
165
|
+
mkdirSync(exeDir, { recursive: true });
|
|
166
|
+
|
|
167
|
+
let targets: string[] = [];
|
|
168
|
+
|
|
169
|
+
if (args.includes('--all')) {
|
|
170
|
+
// Build all targets
|
|
171
|
+
targets = Object.keys(TARGETS);
|
|
172
|
+
console.log('\nBuilding for all platforms...');
|
|
173
|
+
} else if (args.includes('--target')) {
|
|
174
|
+
// Build specific target
|
|
175
|
+
const targetIndex = args.indexOf('--target');
|
|
176
|
+
const targetName = args[targetIndex + 1];
|
|
177
|
+
|
|
178
|
+
if (!targetName || !TARGETS[targetName]) {
|
|
179
|
+
console.error(`Invalid target: ${targetName}`);
|
|
180
|
+
console.error(`Available targets: ${Object.keys(TARGETS).join(', ')}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
targets = [targetName];
|
|
185
|
+
} else {
|
|
186
|
+
// Build for current platform
|
|
187
|
+
const currentTarget = getCurrentPlatformTarget();
|
|
188
|
+
targets = [currentTarget];
|
|
189
|
+
console.log(`\nBuilding for current platform: ${currentTarget}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const results: { target: string; success: boolean }[] = [];
|
|
193
|
+
|
|
194
|
+
for (const targetName of targets) {
|
|
195
|
+
const config = TARGETS[targetName];
|
|
196
|
+
if (!config) {
|
|
197
|
+
console.error(`Unknown target: ${targetName}`);
|
|
198
|
+
results.push({ target: targetName, success: false });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const success = await buildTarget(targetName, config);
|
|
203
|
+
results.push({ target: targetName, success });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Print summary
|
|
207
|
+
console.log('\n' + '='.repeat(60));
|
|
208
|
+
console.log('Build Summary');
|
|
209
|
+
console.log('='.repeat(60));
|
|
210
|
+
|
|
211
|
+
let allSuccess = true;
|
|
212
|
+
for (const result of results) {
|
|
213
|
+
const status = result.success ? 'SUCCESS' : 'FAILED';
|
|
214
|
+
const config = TARGETS[result.target];
|
|
215
|
+
console.log(` ${result.target}: ${status}`);
|
|
216
|
+
if (result.success && config) {
|
|
217
|
+
console.log(` -> ${join(exeDir, config.outputName)}`);
|
|
218
|
+
}
|
|
219
|
+
if (!result.success) allSuccess = false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
console.log('');
|
|
223
|
+
|
|
224
|
+
if (!allSuccess) {
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Print usage instructions for single target builds
|
|
229
|
+
if (targets.length === 1 && results[0]?.success) {
|
|
230
|
+
const config = TARGETS[targets[0]];
|
|
231
|
+
if (config) {
|
|
232
|
+
const binaryPath = join(exeDir, config.outputName);
|
|
233
|
+
console.log('To test the executable:');
|
|
234
|
+
console.log(` ${binaryPath} --help`);
|
|
235
|
+
console.log(` ${binaryPath} --version`);
|
|
236
|
+
console.log('');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
main().catch((error) => {
|
|
242
|
+
console.error('Build failed:', error);
|
|
243
|
+
process.exit(1);
|
|
244
|
+
});
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Generates release.json manifest for CLI releases.
|
|
4
|
+
*
|
|
5
|
+
* This script scans the dist/exe directory for built binaries,
|
|
6
|
+
* calculates SHA256 checksums, and generates a release.json file
|
|
7
|
+
* that the auto-update system uses to check for new versions.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* bun run scripts/generate-release-json.ts
|
|
11
|
+
* bun run scripts/generate-release-json.ts --version 0.5.0
|
|
12
|
+
* bun run scripts/generate-release-json.ts --base-url https://releases.example.com
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
16
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { dirname, join, resolve } from 'node:path';
|
|
18
|
+
|
|
19
|
+
const projectRoot = resolve(dirname(import.meta.path), '..');
|
|
20
|
+
const exeDir = join(projectRoot, 'dist', 'exe');
|
|
21
|
+
const pkgJsonPath = join(projectRoot, 'package.json');
|
|
22
|
+
|
|
23
|
+
interface PlatformInfo {
|
|
24
|
+
url: string;
|
|
25
|
+
sha256: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ReleaseInfo {
|
|
29
|
+
version: string;
|
|
30
|
+
released_at: string;
|
|
31
|
+
platforms: Record<string, PlatformInfo>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseArgs(): { version?: string; baseUrl: string } {
|
|
35
|
+
const args = process.argv.slice(2);
|
|
36
|
+
let version: string | undefined;
|
|
37
|
+
let baseUrl = process.env.RELEASE_BASE_URL ?? 'https://releases.omnara.com';
|
|
38
|
+
|
|
39
|
+
for (let i = 0; i < args.length; i++) {
|
|
40
|
+
if (args[i] === '--version' && args[i + 1]) {
|
|
41
|
+
version = args[i + 1];
|
|
42
|
+
i++;
|
|
43
|
+
} else if (args[i] === '--base-url' && args[i + 1]) {
|
|
44
|
+
baseUrl = args[i + 1];
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return { version, baseUrl };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getPackageVersion(): string {
|
|
53
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8')) as { version?: string };
|
|
54
|
+
return pkgJson.version ?? '0.0.0';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function calculateSha256(filePath: string): string {
|
|
58
|
+
const content = readFileSync(filePath);
|
|
59
|
+
return createHash('sha256').update(content).digest('hex');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function extractPlatform(filename: string): string {
|
|
63
|
+
// omnara-darwin-arm64 -> darwin-arm64
|
|
64
|
+
// omnara-linux-x64-musl -> linux-x64-musl
|
|
65
|
+
// omnara-windows-x64.exe -> windows-x64
|
|
66
|
+
return filename.replace(/^omnara-/, '').replace(/\.exe$/, '');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function main(): void {
|
|
70
|
+
const { version: argVersion, baseUrl } = parseArgs();
|
|
71
|
+
const version = argVersion ?? getPackageVersion();
|
|
72
|
+
|
|
73
|
+
console.log('='.repeat(60));
|
|
74
|
+
console.log('Generating release.json');
|
|
75
|
+
console.log('='.repeat(60));
|
|
76
|
+
console.log(` Version: ${version}`);
|
|
77
|
+
console.log(` Base URL: ${baseUrl}`);
|
|
78
|
+
console.log(` Exe Dir: ${exeDir}`);
|
|
79
|
+
console.log('');
|
|
80
|
+
|
|
81
|
+
if (!existsSync(exeDir)) {
|
|
82
|
+
console.error(`Error: Executable directory not found: ${exeDir}`);
|
|
83
|
+
console.error('Run "bun run scripts/build-executable.ts --all" first.');
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const files = readdirSync(exeDir).filter((f) => {
|
|
88
|
+
return f.startsWith('omnara-') && !f.endsWith('.sha256') && !f.endsWith('.json');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
if (files.length === 0) {
|
|
92
|
+
console.error('Error: No executable files found in dist/exe/');
|
|
93
|
+
console.error('Run "bun run scripts/build-executable.ts --all" first.');
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('Found executables:');
|
|
98
|
+
const platforms: Record<string, PlatformInfo> = {};
|
|
99
|
+
|
|
100
|
+
for (const file of files) {
|
|
101
|
+
const filePath = join(exeDir, file);
|
|
102
|
+
const platform = extractPlatform(file);
|
|
103
|
+
const sha256 = calculateSha256(filePath);
|
|
104
|
+
|
|
105
|
+
platforms[platform] = {
|
|
106
|
+
url: `${baseUrl}/${version}/${file}`,
|
|
107
|
+
sha256,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Also write individual .sha256 file
|
|
111
|
+
const sha256Path = `${filePath}.sha256`;
|
|
112
|
+
writeFileSync(sha256Path, sha256);
|
|
113
|
+
|
|
114
|
+
console.log(` ${platform}:`);
|
|
115
|
+
console.log(` File: ${file}`);
|
|
116
|
+
console.log(` SHA256: ${sha256}`);
|
|
117
|
+
console.log(` URL: ${platforms[platform].url}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const release: ReleaseInfo = {
|
|
121
|
+
version,
|
|
122
|
+
released_at: new Date().toISOString(),
|
|
123
|
+
platforms,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const releasePath = join(exeDir, 'release.json');
|
|
127
|
+
writeFileSync(releasePath, JSON.stringify(release, null, 2) + '\n');
|
|
128
|
+
|
|
129
|
+
console.log('');
|
|
130
|
+
console.log(`Generated: ${releasePath}`);
|
|
131
|
+
console.log('');
|
|
132
|
+
console.log('Release manifest:');
|
|
133
|
+
console.log(JSON.stringify(release, null, 2));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
main();
|