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
123 changes: 123 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
name: CI/Release

on:
push:
branches:
- '**'
tags:
- 'v*'

permissions:
contents: write
Comment on lines +10 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Scope contents: write to the release jobs only.

These permissions apply to every job, including ci on every branch push. That gives install/build steps a write-capable GITHUB_TOKEN when they only need read access.

Proposed fix
-permissions:
-  contents: write
+permissions:
+  contents: read
@@
   release-cli:
     name: Release CLI
     needs: ci
     if: startsWith(github.ref, 'refs/tags/v')
     runs-on: ubuntu-latest
+    permissions:
+      contents: write
@@
   release-desktop:
     name: Desktop (${{ matrix.os }})
     needs: ci
     if: startsWith(github.ref, 'refs/tags/v')
     runs-on: ${{ matrix.os }}
+    permissions:
+      contents: write
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 10 - 11, The workflow currently
sets top-level permissions "contents: write" which grants write access to
GITHUB_TOKEN for all jobs (including "ci"); change this so the default top-level
permissions are read-only (e.g., "contents: read") and then explicitly scope
"contents: write" under only the release job by adding a permissions block
inside the "release" job; update the "ci" job (and any other non-release jobs)
to rely on the default read-only permission so only the "release" job receives
write access.


env:
NODE_VERSION: '20'

jobs:
# ─── CI (runs on every push) ─────────────────────────────
ci:
name: Build & Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install dependencies
run: npm ci

- name: Build CLI
run: npm run build

- name: Typecheck
run: npm run typecheck
Comment on lines +34 to +35
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== root package.json scripts =="
jq '.scripts' package.json

echo "== desktop-app package.json scripts =="
jq '.scripts' desktop-app/package.json

echo "== desktop TypeScript configs =="
jq '{include, references, compilerOptions}' desktop-app/tsconfig.json desktop-app/tsconfig.node.json

Repository: Renderdragonorg/Osmium

Length of output: 1254


🏁 Script executed:

wc -l .github/workflows/release.yml

Repository: Renderdragonorg/Osmium

Length of output: 99


🏁 Script executed:

sed -n '95,110p' .github/workflows/release.yml

Repository: Renderdragonorg/Osmium

Length of output: 555


🏁 Script executed:

cat -n desktop-app/tsconfig.json | head -20

Repository: Renderdragonorg/Osmium

Length of output: 726


🏁 Script executed:

fd 'vite.config' desktop-app

Repository: Renderdragonorg/Osmium

Length of output: 101


🏁 Script executed:

cat -n desktop-app/vite.config.ts 2>/dev/null || cat -n desktop-app/vite.config.js 2>/dev/null || echo "No vite config found"

Repository: Renderdragonorg/Osmium

Length of output: 86


🏁 Script executed:

cat -n desktop-app/electron.vite.config.ts

Repository: Renderdragonorg/Osmium

Length of output: 1184


🏁 Script executed:

cat -n .github/workflows/release.yml

Repository: Renderdragonorg/Osmium

Length of output: 3806


Add a typecheck step for the desktop app in the CI and desktop release workflows.

The root typecheck (lines 34-35) only validates the root project. The desktop-app has its own tsconfig.json with strict settings and a typecheck script (tsc --noEmit), but this is never invoked in the workflow. The npm run build step (line 105) uses electron-vite build, which is a bundler without type checking—TypeScript errors in the main, preload, or renderer code can ship without being caught.

Add - name: Typecheck Desktop / run: npm run typecheck step in the desktop-app directory for both the ci job (after the CLI typecheck) and the release-desktop job (before the build step at line 101).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/release.yml around lines 34 - 35, Add a dedicated desktop
typecheck step that runs the desktop-app's strict TypeScript check by adding a
step named "Typecheck Desktop" that executes npm run typecheck with
working-directory set to desktop-app; insert this step in the ci job immediately
after the existing root/CLI "Typecheck" step and in the release-desktop job
immediately before the "build" step (the existing electron-vite build step), so
the desktop-app's tsconfig (and its tsc --noEmit script) is validated during CI
and release workflows.


# ─── CLI Release (v* tags only) ──────────────────────────
release-cli:
name: Release CLI
needs: ci
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

- name: Install dependencies
run: npm ci

- name: Build CLI
run: npm run build

- name: Package tarball
run: npm pack

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: '*.tgz'
generate_release_notes: true

# ─── Desktop Release ───────────────────────────────────
release-desktop:
name: Desktop (${{ matrix.os }})
needs: ci
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
build-flag: --win
- os: macos-latest
build-flag: --mac
- os: ubuntu-latest
build-flag: --linux

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}

# 1. Build CLI first (desktop app bundles it via extraResources)
- name: Install CLI dependencies
run: npm ci

- name: Build CLI
run: npm run build

# 2. Build Desktop app
- name: Install Desktop dependencies
working-directory: desktop-app
run: npm ci

- name: Build Desktop (electron-vite)
working-directory: desktop-app
env:
CONVEX_URL: ${{ secrets.CONVEX_URL }}
run: npm run build

# 3. Package with electron-builder
- name: Package Desktop
working-directory: desktop-app
run: npx electron-builder ${{ matrix.build-flag }} --publish never

# 4. Upload installers to the release
- name: Upload to GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
desktop-app/dist/*.exe
desktop-app/dist/*.dmg
desktop-app/dist/*.AppImage
desktop-app/dist/*.deb
desktop-app/dist/*.zip
desktop-app/dist/*.tar.gz

10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,12 @@ node_modules/
dist/
.env
*.tgz
plans/
plans/
out/
desktop-app/node_modules/
desktop-app/dist/
desktop-app/out/
desktop-app/.env
results.json
tmp/
.env.local
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Osmium CLI 💿

**Osmium** is an AI-powered music copyright checker. It orchestrates a sophisticated multi-agent AI pipeline to resolve track ownership by consulting Spotify, PRO registries (ASCAP, BMI, SESAC, Copyright Office), audio fingerprinting (AcoustID), metadata databases (MusicBrainz, Discogs), and sample databases.
**Osmium** is a music copyright checker By Renderdragon. It orchestrates a sophisticated multi-agent AI pipeline to resolve track ownership by consulting Spotify, PRO registries (ASCAP, BMI, SESAC, Copyright Office), audio fingerprinting (AcoustID), metadata databases (MusicBrainz, Discogs), and sample databases.

Finally, it synthesizes all this data using an LLM (via OpenRouter) to provide a definitive, human-readable answer on whether a specific track requires a license and the steps to obtain it.

Expand Down
90 changes: 90 additions & 0 deletions convex/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Welcome to your Convex functions directory!

Write your Convex functions here.
See https://docs.convex.dev/functions for more.

A query function that takes two arguments looks like:

```ts
// convex/myFunctions.ts
import { query } from "./_generated/server";
import { v } from "convex/values";

export const myQueryFunction = query({
// Validators for arguments.
args: {
first: v.number(),
second: v.string(),
},

// Function implementation.
handler: async (ctx, args) => {
// Read the database as many times as you need here.
// See https://docs.convex.dev/database/reading-data.
const documents = await ctx.db.query("tablename").collect();

// Arguments passed from the client are properties of the args object.
console.log(args.first, args.second);

// Write arbitrary JavaScript here: filter, aggregate, build derived data,
// remove non-public properties, or create new objects.
return documents;
},
});
```

Using this query function in a React component looks like:

```ts
const data = useQuery(api.myFunctions.myQueryFunction, {
first: 10,
second: "hello",
});
```

A mutation function looks like:

```ts
// convex/myFunctions.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";

export const myMutationFunction = mutation({
// Validators for arguments.
args: {
first: v.string(),
second: v.string(),
},

// Function implementation.
handler: async (ctx, args) => {
// Insert or modify documents in the database here.
// Mutations can also read from the database like queries.
// See https://docs.convex.dev/database/writing-data.
const message = { body: args.first, author: args.second };
const id = await ctx.db.insert("messages", message);

// Optionally, return a value from your mutation.
return await ctx.db.get("messages", id);
},
});
```

Using this mutation function in a React component looks like:

```ts
const mutation = useMutation(api.myFunctions.myMutationFunction);
function handleButtonPress() {
// fire and forget, the most common way to use mutations
mutation({ first: "Hello!", second: "me" });
// OR
// use the result once the mutation has completed
mutation({ first: "Hello!", second: "me" }).then((result) =>
console.log(result),
);
}
```

Use the Convex CLI to push your functions to a deployment. See everything
the Convex CLI can do by running `npx convex -h` in your project root
directory. To learn more, launch the docs with `npx convex docs`.
49 changes: 49 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* eslint-disable */
/**
* Generated `api` utility.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/

import type * as secrets from "../secrets.js";

import type {
ApiFromModules,
FilterApi,
FunctionReference,
} from "convex/server";

declare const fullApi: ApiFromModules<{
secrets: typeof secrets;
}>;

/**
* A utility for referencing Convex functions in your app's public API.
*
* Usage:
* ```js
* const myFunctionReference = api.myModule.myFunction;
* ```
*/
export declare const api: FilterApi<
typeof fullApi,
FunctionReference<any, "public">
>;

/**
* A utility for referencing Convex functions in your app's internal API.
*
* Usage:
* ```js
* const myFunctionReference = internal.myModule.myFunction;
* ```
*/
export declare const internal: FilterApi<
typeof fullApi,
FunctionReference<any, "internal">
>;

export declare const components: {};
23 changes: 23 additions & 0 deletions convex/_generated/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable */
/**
* Generated `api` utility.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/

import { anyApi, componentsGeneric } from "convex/server";

/**
* A utility for referencing Convex functions in your app's API.
*
* Usage:
* ```js
* const myFunctionReference = api.myModule.myFunction;
* ```
*/
export const api = anyApi;
export const internal = anyApi;
export const components = componentsGeneric();
58 changes: 58 additions & 0 deletions convex/_generated/dataModel.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* eslint-disable */
/**
* Generated data model types.
*
* THIS CODE IS AUTOMATICALLY GENERATED.
*
* To regenerate, run `npx convex dev`.
* @module
*/

import { AnyDataModel } from "convex/server";
import type { GenericId } from "convex/values";

/**
* No `schema.ts` file found!
*
* This generated code has permissive types like `Doc = any` because
* Convex doesn't know your schema. If you'd like more type safety, see
* https://docs.convex.dev/using/schemas for instructions on how to add a
* schema file.
*
* After you change a schema, rerun codegen with `npx convex dev`.
*/

/**
* The names of all of your Convex tables.
*/
export type TableNames = string;

/**
* The type of a document stored in Convex.
*/
export type Doc = any;

/**
* An identifier for a document in Convex.
*
* Convex documents are uniquely identified by their `Id`, which is accessible
* on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).
*
* Documents can be loaded using `db.get(tableName, id)` in query and mutation functions.
*
* IDs are just strings at runtime, but this type can be used to distinguish them from other
* strings when type checking.
*/
export type Id<TableName extends TableNames = TableNames> =
GenericId<TableName>;

/**
* A type describing your Convex data model.
*
* This type includes information about what tables you have, the type of
* documents stored in those tables, and the indexes defined on them.
*
* This type is used to parameterize methods like `queryGeneric` and
* `mutationGeneric` to make them type-safe.
*/
export type DataModel = AnyDataModel;
Loading