Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c812d49250 |
@@ -12,7 +12,7 @@
|
||||
<a href="https://learnharness.ai">Download</a> ·
|
||||
<a href="#how-it-works">How it works</a> ·
|
||||
<a href="docs/development.md">Documentation</a> ·
|
||||
<a href="https://github.com/amosblomqvist/learnharness/issues">Issues</a> ·
|
||||
<a href="https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/issues">Issues</a> ·
|
||||
<a href="https://discord.gg/5tCHHQEB2e">Discord</a>
|
||||
</p>
|
||||
|
||||
@@ -43,7 +43,7 @@ Future teaching sessions can read the graph instead of starting from scratch. Pr
|
||||
|
||||
## Download and get started
|
||||
|
||||
**[Download from learnharness.ai](https://learnharness.ai)** · [GitHub Releases](https://github.com/amosblomqvist/learnharness/releases)
|
||||
**[Download from learnharness.ai](https://learnharness.ai)** · [Gitea Releases](https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/releases)
|
||||
|
||||
- **macOS (Apple Silicon):** signed and notarized DMG. Drag the app to Applications.
|
||||
- **Windows (x64):** run the installer. It is currently unsigned; Windows may show an “unknown publisher” or SmartScreen warning.
|
||||
@@ -72,7 +72,7 @@ npm test # deterministic, no LLM, no network
|
||||
|
||||
See the [development guide](docs/development.md), [architecture](docs/architecture.md), and [desktop release guide](docs/desktop-release.md).
|
||||
|
||||
Questions, bug reports, and feature requests: [GitHub Issues](https://github.com/amosblomqvist/learnharness/issues).
|
||||
Questions, bug reports, and feature requests: [Gitea Issues](https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/issues).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Windows signing is deliberately disabled for this first release. SmartScreen may
|
||||
|
||||
The custom app bar stays on all three platforms. macOS keeps its left-side traffic lights; Windows/Linux use Electron's native right-side Window Controls Overlay, with CSS space reserved from the overlay's actual geometry. Windows/Linux hide the native menu until Alt is pressed. macOS keeps its system menu.
|
||||
|
||||
Icons are per-platform (2026-10-13): macOS compiles the Icon Composer document `build/icon.icon` via actool into `Assets.car` (macOS 26 Liquid Glass) plus a fallback icns that `scripts/after-pack.cjs` replaces with the hand-packed full-res `build/icon.icns` (actool's caps at 256px); Windows embeds `build/icon.ico` and Linux uses `build/icon.png`, both generated from the flat design (`build/icon-flat.svg`) by `node scripts/make-icons.mjs`. Building the mac icon needs **macOS 26+ and Xcode 26+** (`actool`): Xcode 26's asset compiler crashes on macOS 15 because its host frameworks lack required symbols. `desktop:dist` injects `DEVELOPER_DIR` locally; release packaging uses `macos-26` and selects Xcode via setup-xcode. The older-OS fallback icon remains included. Linux's desktop identity is `learnharness.desktop`. Public package-maintainer metadata uses the owner's GitHub no-reply address; support goes through [GitHub Issues](https://github.com/amosblomqvist/learnharness/issues).
|
||||
Icons are per-platform (2026-10-13): macOS compiles the Icon Composer document `build/icon.icon` via actool into `Assets.car` (macOS 26 Liquid Glass) plus a fallback icns that `scripts/after-pack.cjs` replaces with the hand-packed full-res `build/icon.icns` (actool's caps at 256px); Windows embeds `build/icon.ico` and Linux uses `build/icon.png`, both generated from the flat design (`build/icon-flat.svg`) by `node scripts/make-icons.mjs`. Building the mac icon needs **macOS 26+ and Xcode 26+** (`actool`): Xcode 26's asset compiler crashes on macOS 15 because its host frameworks lack required symbols. `desktop:dist` injects `DEVELOPER_DIR` locally; release packaging uses `macos-26` and selects Xcode via setup-xcode. The older-OS fallback icon remains included. Linux's desktop identity is `learnharness.desktop`. Public package-maintainer metadata uses the owner's GitHub no-reply address; support goes through [Gitea Issues](https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/issues).
|
||||
|
||||
## Build pipeline
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Gitea releases for this fork
|
||||
|
||||
The release destination is
|
||||
https://git.sorokin-oleg-edu.xyz/replicated/learnharness.
|
||||
https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness.
|
||||
Version **0.6.1 is Linux x64 AppImage-only**; follow the dedicated procedure below.
|
||||
The historical **0.6.0 release targets Linux x64 and Windows x64**, with each platform built
|
||||
and verified on its native operating system. The upstream GitHub workflow and three-platform packaging
|
||||
@@ -12,7 +12,7 @@ Official fork packages use `electron-builder.gitea.yml`, which inherits the
|
||||
upstream packaging and replaces its update origin with:
|
||||
|
||||
```
|
||||
https://git.sorokin-oleg-edu.xyz/replicated/learnharness/releases/download/latest
|
||||
https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/releases/download/latest
|
||||
```
|
||||
|
||||
Gitea's `latest` alias resolves the most recent published, non-prerelease release.
|
||||
|
||||
@@ -5,4 +5,4 @@ publish:
|
||||
provider: generic
|
||||
# Gitea resolves the latest published, non-prerelease release here.
|
||||
# Drafts remain private and do not become an update before publication.
|
||||
url: https://git.sorokin-oleg-edu.xyz/replicated/learnharness/releases/download/latest
|
||||
url: https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/releases/download/latest
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
"name": "Amos Blomqvist",
|
||||
"email": "255718537+amosblomqvist@users.noreply.github.com"
|
||||
},
|
||||
"homepage": "https://github.com/amosblomqvist/learnharness",
|
||||
"homepage": "https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness",
|
||||
"desktopName": "learnharness.desktop",
|
||||
"description": "An agent harness for learning: adaptive teaching, practice, and a persistent knowledge graph.",
|
||||
"type": "module",
|
||||
|
||||
@@ -14,7 +14,7 @@ const require = createRequire(import.meta.url);
|
||||
const yaml = require("js-yaml");
|
||||
const ROOT = path.resolve(import.meta.dirname, "..");
|
||||
export const SERVER = "https://git.sorokin-oleg-edu.xyz";
|
||||
export const REPOSITORY = "replicated/learnharness";
|
||||
export const REPOSITORY = "sorokin.oleg.edu/learnharness";
|
||||
export const UPDATE_URL = `${SERVER}/${REPOSITORY}/releases/download/latest`;
|
||||
const API = `${SERVER}/api/v1/repos/${REPOSITORY}`;
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ describe("desktop packaging policy", () => {
|
||||
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
||||
assert.equal(pkg.author.name, "Amos Blomqvist");
|
||||
assert.equal(pkg.author.email, "255718537+amosblomqvist@users.noreply.github.com");
|
||||
assert.equal(pkg.homepage, "https://github.com/amosblomqvist/learnharness");
|
||||
assert.equal(pkg.homepage, "https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness");
|
||||
assert.equal(pkg.desktopName, "learnharness.desktop");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ async function fixture(name: string, appimageOnly = false, fixtureVersion = vers
|
||||
|
||||
test("Linux release inventory and Gitea feed match the current release scope", () => {
|
||||
assert.deepEqual(names, ["LearnHarness-0.4.0-x86_64.AppImage", "LearnHarness-0.4.0-amd64.deb", "latest-linux.yml", "SHA256SUMS"]);
|
||||
assert.equal(UPDATE_URL, "https://git.sorokin-oleg-edu.xyz/replicated/learnharness/releases/download/latest");
|
||||
assert.equal(UPDATE_URL, "https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/releases/download/latest");
|
||||
assert.throws(() => expectedLinuxAssets("0.4.0-preview"));
|
||||
});
|
||||
test("local verification checks actual installer bytes, updater hashes, sizes and checksum manifest", async () => {
|
||||
@@ -61,7 +61,7 @@ test("empty files and arbitrary downloads cannot pass as installers", async () =
|
||||
test("remote verification downloads and hashes every draft attachment, and refuses published or incomplete releases", async () => {
|
||||
const directory = await fixture("remote");
|
||||
const inventory = await verifyLocal(directory, version);
|
||||
const assets = inventory.map((asset) => ({ ...asset, browser_download_url: `https://git.sorokin-oleg-edu.xyz/replicated/learnharness/releases/download/v0.4.0/${asset.name}` }));
|
||||
const assets = inventory.map((asset) => ({ ...asset, browser_download_url: `https://git.sorokin-oleg-edu.xyz/sorokin.oleg.edu/learnharness/releases/download/v0.4.0/${asset.name}` }));
|
||||
const release = { draft: true, prerelease: false, assets };
|
||||
const request = async (input: URL | RequestInfo) => {
|
||||
const url = input instanceof URL ? input : new URL(String(input));
|
||||
@@ -184,10 +184,10 @@ function mockedReleaseCommand(directory: string, command: string, options: strin
|
||||
const { version } = JSON.parse(await fs.readFile(${JSON.stringify(path.resolve(import.meta.dirname, "../package.json"))}, "utf8"));
|
||||
const tag = "v" + version;
|
||||
const server = "https://git.sorokin-oleg-edu.xyz";
|
||||
const api = "/api/v1/repos/replicated/learnharness";
|
||||
const api = "/api/v1/repos/sorokin.oleg.edu/learnharness";
|
||||
const calls = { api: [], uploads: [], downloads: [], edits: [] };
|
||||
const files = (await fs.readdir(directory)).filter(name => name.endsWith(".AppImage") || name.endsWith(".deb") || ["latest-linux.yml", "SHA256SUMS"].includes(name));
|
||||
const assets = await Promise.all(files.map(async name => ({ name, size: (await fs.stat(path.join(directory, name))).size, browser_download_url: server + "/replicated/learnharness/releases/download/" + tag + "/" + name })));
|
||||
const assets = await Promise.all(files.map(async name => ({ name, size: (await fs.stat(path.join(directory, name))).size, browser_download_url: server + "/sorokin.oleg.edu/learnharness/releases/download/" + tag + "/" + name })));
|
||||
const release = { id: 1, tag_name: tag, draft: scenario !== "published", prerelease: scenario === "prerelease", assets: scenario === "new" ? [] : [...assets], html_url: server + "/draft" };
|
||||
if (scenario === "missing-asset") release.assets.pop();
|
||||
if (scenario === "duplicate-asset") release.assets.push(assets[0]);
|
||||
@@ -228,7 +228,7 @@ function mockedReleaseCommand(directory: string, command: string, options: strin
|
||||
return json(asset);
|
||||
}
|
||||
if (method === "GET" && url.pathname === api + "/releases/1") return json(release);
|
||||
} else if (method === "GET" && url.pathname.startsWith("/replicated/learnharness/releases/download/" + tag + "/")) {
|
||||
} else if (method === "GET" && url.pathname.startsWith("/sorokin.oleg.edu/learnharness/releases/download/" + tag + "/")) {
|
||||
const name = decodeURIComponent(path.basename(url.pathname));
|
||||
assert(files.includes(name), "download outside expected fixture");
|
||||
calls.downloads.push(name);
|
||||
|
||||
@@ -51,7 +51,7 @@ test("Gitea updater identity and release tooling are preserved", () => {
|
||||
const config = source("electron-builder.gitea.yml");
|
||||
assert.match(config, /extends: \.\/electron-builder\.yml/);
|
||||
assert.match(config, /provider: generic/);
|
||||
assert.match(config, /url: https:\/\/server2\.sorokin-oleg-edu\.xyz\/replicated\/learnharness\/releases\/download\/latest/);
|
||||
assert.match(config, /url: https:\/\/git\.sorokin-oleg-edu\.xyz\/sorokin\.oleg\.edu\/learnharness\/releases\/download\/latest/);
|
||||
assert.ok(tracked.has("scripts/gitea-release.mjs"));
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user