Skip to content
Merged
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 .github/workflows/ci-fork.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ concurrency:

env:
BUN_VERSION: "1.3.10"
MILADY_SKIP_LOCAL_UPSTREAMS: "1"

jobs:
lint:
Expand Down
6 changes: 0 additions & 6 deletions .github/workflows/task-agent-cross-platform-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,7 @@ jobs:
with:
submodules: false

- name: Initialize Windows-safe workspace submodules
if: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: git submodule update --init --depth 1 plugins/plugin-agent-orchestrator plugins/plugin-sql

- name: Initialize tracked workspace submodules
if: ${{ matrix.os != 'windows-latest' }}
run: node scripts/init-submodules.mjs

- name: Setup Node.js
Expand Down
18 changes: 3 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
# --ignore-scripts, so we run it explicitly before buf generate.
- name: Generate protobuf types
run: |
if [ -d eliza/packages/schemas ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
if [ -d eliza/packages/schemas ] && [ -f eliza/packages/schemas/buf.gen.yaml ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
cd eliza/packages/schemas
node node_modules/@bufbuild/buf/install.js 2>/dev/null || true
npx --yes @bufbuild/buf@1.67.0 generate
Expand Down Expand Up @@ -119,7 +119,7 @@ jobs:
# --ignore-scripts, so we run it explicitly before buf generate.
- name: Generate protobuf types
run: |
if [ -d eliza/packages/schemas ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
if [ -d eliza/packages/schemas ] && [ -f eliza/packages/schemas/buf.gen.yaml ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
cd eliza/packages/schemas
node node_modules/@bufbuild/buf/install.js 2>/dev/null || true
npx --yes @bufbuild/buf@1.67.0 generate
Expand Down Expand Up @@ -172,13 +172,7 @@ jobs:
# Published-only CI restores the required non-eliza submodules explicitly.
submodules: false

- name: Initialize Windows-safe workspace submodules
if: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: git submodule update --init --depth 1 plugins/plugin-agent-orchestrator

- name: Initialize tracked workspace submodules
if: ${{ matrix.os != 'windows-latest' }}
run: node scripts/init-submodules.mjs

- name: Setup Node.js
Expand Down Expand Up @@ -244,13 +238,7 @@ jobs:
# Published-only CI restores the required non-eliza submodules explicitly.
submodules: false

- name: Initialize Windows-safe workspace submodules
if: ${{ matrix.os == 'windows-latest' }}
shell: bash
run: git submodule update --init --depth 1 plugins/plugin-agent-orchestrator

- name: Initialize tracked workspace submodules
if: ${{ matrix.os != 'windows-latest' }}
run: node scripts/init-submodules.mjs

- name: Setup Node.js
Expand Down Expand Up @@ -490,7 +478,7 @@ jobs:
# --ignore-scripts, so we run it explicitly before buf generate.
- name: Generate protobuf types
run: |
if [ -d eliza/packages/schemas ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
if [ -d eliza/packages/schemas ] && [ -f eliza/packages/schemas/buf.gen.yaml ] && [ ! -d eliza/packages/typescript/src/types/generated ]; then
cd eliza/packages/schemas
node node_modules/@bufbuild/buf/install.js 2>/dev/null || true
npx --yes @bufbuild/buf@1.67.0 generate
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/windows-desktop-preload-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ jobs:
with:
submodules: false

- name: Initialize Windows-safe workspace submodules
shell: bash
run: git submodule update --init --depth 1 plugins/plugin-agent-orchestrator
- name: Initialize workspace submodules
run: node scripts/init-submodules.mjs

- name: Enable long paths (Windows)
run: git config --global core.longpaths true
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/windows-dev-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Initialize Windows-safe workspace submodules
shell: bash
run: git submodule update --init --depth 1 plugins/plugin-agent-orchestrator
- name: Initialize workspace submodules
run: node scripts/init-submodules.mjs

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
23 changes: 18 additions & 5 deletions apps/app/electrobun/src/__tests__/agent.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from "node:path";

import { DEFAULT_DESKTOP_API_PORT } from "@miladyai/shared/runtime-env";
import {
afterEach,
Expand Down Expand Up @@ -71,9 +73,16 @@ vi.mock("../native/loopback-port", () => ({
const mockSpawn = vi.fn();
const mockSleep = vi.fn(() => Promise.resolve());

// Bun global is non-configurable on globalThis but Bun.spawn and Bun.sleep are writable; assign directly.
(Bun as unknown as { spawn: unknown }).spawn = mockSpawn;
(Bun as unknown as { sleep: unknown }).sleep = mockSleep;
type BunLike = { spawn: unknown; sleep: unknown };

function bunGlobal(): BunLike {
const globalState = globalThis as typeof globalThis & { Bun?: BunLike };
globalState.Bun ??= { spawn: vi.fn(), sleep: vi.fn(() => Promise.resolve()) };
return globalState.Bun;
}

bunGlobal().spawn = mockSpawn;
bunGlobal().sleep = mockSleep;

// Mock fetch for health checks
const mockFetch = vi.fn();
Expand Down Expand Up @@ -456,7 +465,9 @@ describe("AgentManager", () => {
prefix: "../../escape\\..\\report bundle",
});

expect(bundle.directory).toContain("/bug-reports/escape-report-bundle-");
expect(bundle.directory).toContain(
`${path.sep}bug-reports${path.sep}escape-report-bundle-`,
);
expect(bundle.directory).not.toContain("../");
expect(bundle.directory).not.toContain("..\\");
});
Expand Down Expand Up @@ -905,7 +916,9 @@ describe("AgentManager", () => {

const spawnOptions = mockSpawn.mock.calls[0]?.[1];
expect(spawnOptions?.env?.NODE_PATH).toBe(
"/mock/milady-dist/node_modules:/mock/node_modules",
["/mock/milady-dist/node_modules", "/mock/node_modules"].join(
path.delimiter,
),
);
} finally {
if (originalNodePath === undefined) {
Expand Down
28 changes: 16 additions & 12 deletions apps/app/electrobun/src/__tests__/background-notice.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import path from "node:path";

import { describe, expect, it } from "vitest";
import {
BACKGROUND_NOTICE_MARKER_FILE,
Expand All @@ -8,22 +10,24 @@ import {
} from "../background-notice";

describe("background notice", () => {
const userDataDir = path.join("/tmp", "milady");
const otherDataDir = path.join("/tmp", "other");
const markerPath = path.join(userDataDir, BACKGROUND_NOTICE_MARKER_FILE);

it("resolves the marker path under the userData directory", () => {
expect(resolveBackgroundNoticeMarkerPath("/tmp/milady")).toBe(
`/tmp/milady/${BACKGROUND_NOTICE_MARKER_FILE}`,
);
expect(resolveBackgroundNoticeMarkerPath(userDataDir)).toBe(markerPath);
});

it("reports whether the background notice marker already exists", () => {
const seenPaths = new Set([`/tmp/milady/${BACKGROUND_NOTICE_MARKER_FILE}`]);
const seenPaths = new Set([markerPath]);
const fileSystem = {
existsSync: (filePath: string) => seenPaths.has(filePath),
mkdirSync: () => {},
writeFileSync: () => {},
};

expect(hasSeenBackgroundNotice(fileSystem, "/tmp/milady")).toBe(true);
expect(hasSeenBackgroundNotice(fileSystem, "/tmp/other")).toBe(false);
expect(hasSeenBackgroundNotice(fileSystem, userDataDir)).toBe(true);
expect(hasSeenBackgroundNotice(fileSystem, otherDataDir)).toBe(false);
});

it("writes the marker file when the background notice is shown", () => {
Expand Down Expand Up @@ -52,18 +56,18 @@ describe("background notice", () => {
},
};

const markerPath = markBackgroundNoticeSeen(fileSystem, "/tmp/milady");
const createdMarkerPath = markBackgroundNoticeSeen(fileSystem, userDataDir);

expect(markerPath).toBe(`/tmp/milady/${BACKGROUND_NOTICE_MARKER_FILE}`);
expect(createdMarkerPath).toBe(markerPath);
expect(mkdirCalls).toEqual([
{
dirPath: "/tmp/milady",
dirPath: userDataDir,
recursive: true,
},
]);
expect(writeCalls).toEqual([
{
filePath: `/tmp/milady/${BACKGROUND_NOTICE_MARKER_FILE}`,
filePath: markerPath,
data: '{"seen":true}\n',
encoding: "utf8",
},
Expand All @@ -84,7 +88,7 @@ describe("background notice", () => {
expect(
showBackgroundNoticeOnce({
fileSystem,
userDataDir: "/tmp/milady",
userDataDir,
showNotification: (options) => {
notifications.push(options);
},
Expand All @@ -93,7 +97,7 @@ describe("background notice", () => {
expect(
showBackgroundNoticeOnce({
fileSystem,
userDataDir: "/tmp/milady",
userDataDir,
showNotification: (options) => {
notifications.push(options);
},
Expand Down
11 changes: 9 additions & 2 deletions apps/app/electrobun/src/__tests__/kitchen-sink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -607,11 +607,18 @@ vi.mock("../native/desktop", async () => {
});

function stubBunGlobal(): void {
const globalState = globalThis as typeof globalThis & {
Bun?: { spawn: unknown; sleep: unknown };
};
globalState.Bun ??= {
spawn: vi.fn(),
sleep: vi.fn(() => Promise.resolve()),
};
// Bun global is non-configurable on globalThis but Bun.spawn and Bun.sleep
// are writable data properties, so direct assignment works.
// Bun.version is non-writable/non-configurable — tests that read it will see
// the real runtime version, which is acceptable since no test asserts its value.
(Bun as unknown as { spawn: unknown }).spawn = vi.fn(() => ({
globalState.Bun.spawn = vi.fn(() => ({
exited: Promise.resolve(0),
stdout: new ReadableStream({
start(c) {
Expand All @@ -627,7 +634,7 @@ function stubBunGlobal(): void {
pid: 12345,
kill: vi.fn(),
}));
(Bun as unknown as { sleep: unknown }).sleep = vi.fn(() => Promise.resolve());
globalState.Bun.sleep = vi.fn(() => Promise.resolve());
}

stubBunGlobal();
Expand Down
21 changes: 12 additions & 9 deletions apps/app/electrobun/src/__tests__/renderer-static.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import path from "node:path";

import { describe, expect, it } from "vitest";

import { resolveRendererAsset } from "../renderer-static";

describe("resolveRendererAsset", () => {
const rendererDir = "/tmp/renderer";
const rendererDir = path.join("/tmp", "renderer");
const indexPath = path.join(rendererDir, "index.html");

function resolve(paths: Record<string, "file" | "dir">, urlPath: string) {
return resolveRendererAsset({
Expand All @@ -19,14 +22,14 @@ describe("resolveRendererAsset", () => {
it("serves precompressed assets when the plain file is missing", () => {
const result = resolve(
{
"/tmp/renderer/index.html": "file",
"/tmp/renderer/animations/idle.glb.gz": "file",
[indexPath]: "file",
[path.join(rendererDir, "animations", "idle.glb.gz")]: "file",
},
"/animations/idle.glb",
);

expect(result).toEqual({
filePath: "/tmp/renderer/animations/idle.glb.gz",
filePath: path.join(rendererDir, "animations", "idle.glb.gz"),
isGzipped: true,
mimeExt: ".glb",
});
Expand All @@ -35,14 +38,14 @@ describe("resolveRendererAsset", () => {
it("falls back to plain assets when packaged wrappers drop the .gz suffix", () => {
const result = resolve(
{
"/tmp/renderer/index.html": "file",
"/tmp/renderer/vrms/milady-1.vrm": "file",
[indexPath]: "file",
[path.join(rendererDir, "vrms", "milady-1.vrm")]: "file",
},
"/vrms/milady-1.vrm.gz",
);

expect(result).toEqual({
filePath: "/tmp/renderer/vrms/milady-1.vrm",
filePath: path.join(rendererDir, "vrms", "milady-1.vrm"),
isGzipped: false,
mimeExt: ".vrm",
});
Expand All @@ -51,13 +54,13 @@ describe("resolveRendererAsset", () => {
it("falls back to index.html for traversal attempts", () => {
const result = resolve(
{
"/tmp/renderer/index.html": "file",
[indexPath]: "file",
},
"/../../etc/passwd",
);

expect(result).toEqual({
filePath: "/tmp/renderer/index.html",
filePath: indexPath,
isGzipped: false,
mimeExt: ".html",
});
Expand Down
41 changes: 25 additions & 16 deletions apps/app/electrobun/src/__tests__/startup-trace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ function readJson(filePath: string) {
}

describe("startup trace", () => {
const darwinBundlePath = path.join("/Applications", "Milady-canary.app");
const darwinExecPath = path.join(
darwinBundlePath,
"Contents",
"MacOS",
"launcher",
);
const darwinBootstrapPath = path.join(
darwinBundlePath,
"Contents",
"Resources",
"startup-session.json",
);
const winBundlePath = path.join(
"/Users/test/AppData/Local/com.miladyai.milady/canary/self-extraction",
"Milady-canary",
);
const winExecPath = path.join(winBundlePath, "bin", "launcher.exe");
const winBootstrapPath = path.join(winBundlePath, "startup-session.json");
let tempDir: string;

beforeEach(() => {
Expand All @@ -46,7 +65,7 @@ describe("startup trace", () => {
"main_start",
{
pid: 123,
exec_path: "/Applications/Milady-canary.app/Contents/MacOS/launcher",
exec_path: darwinExecPath,
},
env,
);
Expand All @@ -56,7 +75,7 @@ describe("startup trace", () => {
expect(state.session_id).toBe("session-a");
expect(state.phase).toBe("main_start");
expect(state.pid).toBe(123);
expect(state.bundle_path).toBe("/Applications/Milady-canary.app");
expect(state.bundle_path).toBe(darwinBundlePath);

const events = fs
.readFileSync(env.MILADY_STARTUP_EVENTS_FILE as string, "utf8")
Expand Down Expand Up @@ -187,21 +206,11 @@ describe("startup trace", () => {
});

it("derives bundle-local bootstrap sidecars from packaged launcher paths", () => {
expect(
resolveStartupTraceBootstrapFile(
"/Applications/Milady-canary.app/Contents/MacOS/launcher",
"darwin",
),
).toBe(
"/Applications/Milady-canary.app/Contents/Resources/startup-session.json",
expect(resolveStartupTraceBootstrapFile(darwinExecPath, "darwin")).toBe(
darwinBootstrapPath,
);
expect(
resolveStartupTraceBootstrapFile(
"/Users/test/AppData/Local/com.miladyai.milady/canary/self-extraction/Milady-canary/bin/launcher.exe",
"win32",
),
).toBe(
"/Users/test/AppData/Local/com.miladyai.milady/canary/self-extraction/Milady-canary/startup-session.json",
expect(resolveStartupTraceBootstrapFile(winExecPath, "win32")).toBe(
winBootstrapPath,
);
});

Expand Down
Loading
Loading