feat: add semantic-release for automated versioning and help dialog

- Set up semantic-release with zero-touch CI pipeline on push to main
- Add version sync script to keep all package.json files and APP_VERSION
  constant in sync automatically
- Consolidate Docker publishing into single tag-triggered workflow that
  pushes to both Docker Hub and ghcr.io with semver tags
- Add help dialog with keyboard shortcuts, getting started guide, and
  resource links
- Sync all versions to 0.2.1 to match Docker Hub latest
This commit is contained in:
Siddharth Kumar Sah
2026-03-22 21:25:14 +08:00
parent fb1d366d5f
commit 4807bd2726
22 changed files with 2359 additions and 82 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@stirling-image/ai",
"version": "0.0.1",
"version": "0.2.1",
"private": true,
"type": "module",
"main": "./src/index.ts",
-2
View File
@@ -68,10 +68,8 @@ def main():
}
)
)
sys.exit(1)
except Exception as e:
print(json.dumps({"success": False, "error": str(e)}))
sys.exit(1)
if __name__ == "__main__":
+25 -15
View File
@@ -1,6 +1,8 @@
import { runPythonScript } from "./bridge.js";
import { writeFile, readFile } from "node:fs/promises";
import { writeFile, readFile, unlink } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { randomUUID } from "node:crypto";
export interface RemoveBackgroundOptions {
model?: string;
@@ -12,22 +14,30 @@ export async function removeBackground(
outputDir: string,
options: RemoveBackgroundOptions = {},
): Promise<Buffer> {
const inputPath = join(outputDir, "input_bg.png");
const outputPath = join(outputDir, "output_bg.png");
const id = randomUUID();
const inputPath = join(tmpdir(), `rembg_in_${id}.png`);
const outputPath = join(outputDir, `rembg_out_${id}.png`);
await writeFile(inputPath, inputBuffer);
// BiRefNet models need longer timeout (up to 10 min for first load)
const timeout = options.model?.startsWith("birefnet") ? 600000 : 300000;
const { stdout } = await runPythonScript("remove_bg.py", [
inputPath,
outputPath,
JSON.stringify(options),
], timeout);
try {
// BiRefNet models need longer timeout (up to 10 min for first load)
const timeout = options.model?.startsWith("birefnet") ? 600000 : 300000;
const { stdout } = await runPythonScript("remove_bg.py", [
inputPath,
outputPath,
JSON.stringify(options),
], timeout);
const result = JSON.parse(stdout);
if (!result.success) {
throw new Error(result.error || "Background removal failed");
const result = JSON.parse(stdout);
if (!result.success) {
throw new Error(result.error || "Background removal failed");
}
const outputBuffer = await readFile(outputPath);
return outputBuffer;
} finally {
// Clean up temp files
await unlink(inputPath).catch(() => {});
await unlink(outputPath).catch(() => {});
}
return readFile(outputPath);
}
+2 -2
View File
@@ -25,8 +25,8 @@ function extractPythonError(error: unknown): string {
stdout?: string;
message?: string;
};
// Try to parse JSON error from stderr or stdout
for (const output of [execError.stderr, execError.stdout]) {
// Try stdout first (Python scripts write JSON errors there), then stderr
for (const output of [execError.stdout, execError.stderr]) {
if (output) {
try {
const parsed = JSON.parse(output.trim());
-2
View File
@@ -1,5 +1,3 @@
export const AI_VERSION = "0.0.1";
export { runPythonScript } from "./bridge.js";
export { removeBackground } from "./background-removal.js";
export type { RemoveBackgroundOptions } from "./background-removal.js";