chore: commit current workspace state
This commit is contained in:
parent
f11c877224
commit
00a118ea71
822 changed files with 3525 additions and 4215 deletions
13
biome.json
13
biome.json
|
|
@ -27,7 +27,16 @@
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true,
|
"recommended": true,
|
||||||
"correctness": {
|
"correctness": {
|
||||||
"noUnreachable": "off"
|
"noUnreachable": "off",
|
||||||
|
"useExhaustiveDependencies": "off"
|
||||||
|
},
|
||||||
|
"a11y": {
|
||||||
|
"noLabelWithoutControl": "off",
|
||||||
|
"noStaticElementInteractions": "off",
|
||||||
|
"noSvgWithoutTitle": "off",
|
||||||
|
"useAriaPropsSupportedByRole": "off",
|
||||||
|
"useKeyWithClickEvents": "off",
|
||||||
|
"useSemanticElements": "off"
|
||||||
},
|
},
|
||||||
"style": {
|
"style": {
|
||||||
"noNonNullAssertion": "off",
|
"noNonNullAssertion": "off",
|
||||||
|
|
@ -35,7 +44,9 @@
|
||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noAssignInExpressions": "off",
|
"noAssignInExpressions": "off",
|
||||||
|
"noArrayIndexKey": "off",
|
||||||
"noControlCharactersInRegex": "off",
|
"noControlCharactersInRegex": "off",
|
||||||
|
"noDocumentCookie": "off",
|
||||||
"noDuplicateTestHooks": "off",
|
"noDuplicateTestHooks": "off",
|
||||||
"noExplicitAny": "off",
|
"noExplicitAny": "off",
|
||||||
"noImplicitAnyLet": "off",
|
"noImplicitAnyLet": "off",
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ is left alone.
|
||||||
|
|
||||||
This is correct for protecting user content but wrong for everything else:
|
This is correct for protecting user content but wrong for everything else:
|
||||||
|
|
||||||
1. **No drift signal.** When SF adds a new template (e.g. `harness/AGENTS.md`,
|
1. **No drift signal.** When SF adds a new template (e.g. `.sf/harness/AGENTS.md`,
|
||||||
`ARCHITECTURE.md`) or improves an existing one (e.g. tightens
|
`ARCHITECTURE.md`) or improves an existing one (e.g. tightens
|
||||||
`RELIABILITY.md`), existing projects never notice. Only newly-bootstrapped
|
`RELIABILITY.md`), existing projects never notice. Only newly-bootstrapped
|
||||||
projects benefit.
|
projects benefit.
|
||||||
|
|
@ -95,7 +95,7 @@ Apply markers to every entry in `SCAFFOLD_FILES`. Categories:
|
||||||
|----------|------------------|------------------|
|
|----------|------------------|------------------|
|
||||||
| Markdown docs | `AGENTS.md`, `ARCHITECTURE.md`, `docs/RELIABILITY.md`, `docs/SECURITY.md`, `docs/DESIGN.md`, `docs/QUALITY_SCORE.md`, `docs/RECORDS_KEEPER.md`, all `*/AGENTS.md` | HTML comment on line 1 |
|
| Markdown docs | `AGENTS.md`, `ARCHITECTURE.md`, `docs/RELIABILITY.md`, `docs/SECURITY.md`, `docs/DESIGN.md`, `docs/QUALITY_SCORE.md`, `docs/RECORDS_KEEPER.md`, all `*/AGENTS.md` | HTML comment on line 1 |
|
||||||
| Frontmatter docs | `.sf/PREFERENCES.md` | Frontmatter fields: `last_synced_with_sf`, `sf_template_state`, `sf_template_hash` (extends prior art in `preferences-template-upgrade.ts`) |
|
| Frontmatter docs | `.sf/PREFERENCES.md` | Frontmatter fields: `last_synced_with_sf`, `sf_template_state`, `sf_template_hash` (extends prior art in `preferences-template-upgrade.ts`) |
|
||||||
| Templates / specs | `docs/design-docs/ADR-TEMPLATE.md`, `harness/specs/bootstrap.md`, `harness/AGENTS.md`, `harness/specs/AGENTS.md`, `harness/evals/AGENTS.md`, `harness/graders/AGENTS.md` | HTML comment on line 1 |
|
| Templates / specs | `docs/design-docs/ADR-TEMPLATE.md`, `.sf/harness/specs/bootstrap.md`, `.sf/harness/AGENTS.md`, `.sf/harness/specs/AGENTS.md`, `.sf/harness/evals/AGENTS.md`, `.sf/harness/graders/AGENTS.md` | HTML comment on line 1 |
|
||||||
| Reference slot text files | `docs/references/*-llms.txt` | HTML comment on line 1 (Markdown comments are valid in plain text consumed by LLMs) |
|
| Reference slot text files | `docs/references/*-llms.txt` | HTML comment on line 1 (Markdown comments are valid in plain text consumed by LLMs) |
|
||||||
| `.siftignore` and similar non-Markdown configs | `.siftignore` | Skip versioning. Sibling file `.sf/scaffold-manifest.json` records the applied version. (Rationale: hash-based legacy match is sufficient; markers in dotfiles fight tooling.) |
|
| `.siftignore` and similar non-Markdown configs | `.siftignore` | Skip versioning. Sibling file `.sf/scaffold-manifest.json` records the applied version. (Rationale: hash-based legacy match is sufficient; markers in dotfiles fight tooling.) |
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
# Bootstrap Spec: Agent Legibility
|
|
||||||
|
|
||||||
Verifies that the SF repo is minimally agent-legible.
|
|
||||||
|
|
||||||
## Criteria
|
|
||||||
|
|
||||||
- [ ] `AGENTS.md` exists at repo root and is non-empty.
|
|
||||||
- [ ] `ARCHITECTURE.md` exists at repo root and describes the system.
|
|
||||||
- [ ] `docs/exec-plans/active/index.md` exists.
|
|
||||||
- [ ] `docs/exec-plans/tech-debt-tracker.md` exists.
|
|
||||||
- [ ] `docs/design-docs/ADR-TEMPLATE.md` exists.
|
|
||||||
- [ ] `harness/specs/` exists with at least this file.
|
|
||||||
|
|
||||||
## Verification command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
for f in AGENTS.md ARCHITECTURE.md docs/exec-plans/active/index.md docs/exec-plans/tech-debt-tracker.md docs/design-docs/ADR-TEMPLATE.md harness/specs/bootstrap.md; do [ -s "$f" ] && echo "OK: $f" || echo "MISSING: $f"; done
|
|
||||||
```
|
|
||||||
|
|
||||||
All lines should start with `OK:` for this spec to pass.
|
|
||||||
5
justfile
5
justfile
|
|
@ -72,11 +72,12 @@ spec name:
|
||||||
printf "# {{name}}\n\n## Job to be done\n\n## Workflow\n\n## Edge cases\n\n## Non-goals\n\n## Verification\n\n\`\`\`bash\n# command that proves this spec passes\n\`\`\`\n" > "${dest}"
|
printf "# {{name}}\n\n## Job to be done\n\n## Workflow\n\n## Edge cases\n\n## Non-goals\n\n## Verification\n\n\`\`\`bash\n# command that proves this spec passes\n\`\`\`\n" > "${dest}"
|
||||||
echo "Created: ${dest}"
|
echo "Created: ${dest}"
|
||||||
|
|
||||||
# Create a new harness spec (usage: just harness-spec "behavior-name")
|
# Create a new SF-local harness spec (usage: just harness-spec "behavior-name")
|
||||||
harness-spec name:
|
harness-spec name:
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
dest="harness/specs/{{name}}.md"
|
dest=".sf/harness/specs/{{name}}.md"
|
||||||
if [ -f "${dest}" ]; then echo "Already exists: ${dest}"; exit 1; fi
|
if [ -f "${dest}" ]; then echo "Already exists: ${dest}"; exit 1; fi
|
||||||
|
mkdir -p "$(dirname "${dest}")"
|
||||||
printf "# Harness Spec: {{name}}\n\n## Behavior\n\n## Verification command\n\n\`\`\`bash\n\n\`\`\`\n\n## Pass criteria\n\n" > "${dest}"
|
printf "# Harness Spec: {{name}}\n\n## Behavior\n\n## Verification command\n\n\`\`\`bash\n\n\`\`\`\n\n## Pass criteria\n\n" > "${dest}"
|
||||||
echo "Created: ${dest}"
|
echo "Created: ${dest}"
|
||||||
|
|
|
||||||
|
|
@ -98,8 +98,10 @@
|
||||||
"typecheck:extensions": "npm run check:versioned-json && tsc --noEmit --project tsconfig.extensions.json",
|
"typecheck:extensions": "npm run check:versioned-json && tsc --noEmit --project tsconfig.extensions.json",
|
||||||
"check:sf-inventory": "node scripts/check-sf-extension-inventory.mjs",
|
"check:sf-inventory": "node scripts/check-sf-extension-inventory.mjs",
|
||||||
"check:versioned-json": "node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
|
"check:versioned-json": "node scripts/check-versioned-json.mjs && npm run check:sf-inventory",
|
||||||
"lint": "npm run check:versioned-json && biome lint src/",
|
"format": "biome format --write .",
|
||||||
"lint:fix": "npm run check:versioned-json && biome lint src/ --write",
|
"format:check": "biome format .",
|
||||||
|
"lint": "npm run check:versioned-json && biome check .",
|
||||||
|
"lint:fix": "npm run check:versioned-json && biome check --write .",
|
||||||
"pipeline:version-stamp": "node scripts/version-stamp.mjs",
|
"pipeline:version-stamp": "node scripts/version-stamp.mjs",
|
||||||
"release:changelog": "node scripts/generate-changelog.mjs",
|
"release:changelog": "node scripts/generate-changelog.mjs",
|
||||||
"release:bump": "node scripts/bump-version.mjs",
|
"release:bump": "node scripts/bump-version.mjs",
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
|
||||||
PermissionFlagsBits,
|
|
||||||
type Guild,
|
|
||||||
type CategoryChannel,
|
type CategoryChannel,
|
||||||
type TextChannel,
|
ChannelType,
|
||||||
|
type Guild,
|
||||||
type GuildBasedChannel,
|
type GuildBasedChannel,
|
||||||
|
PermissionFlagsBits,
|
||||||
|
type TextChannel,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import type { Logger } from "./logger.js";
|
import type { Logger } from "./logger.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { parseArgs } from "node:util";
|
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { resolveConfigPath, loadConfig } from "./config.js";
|
import { parseArgs } from "node:util";
|
||||||
import { Logger } from "./logger.js";
|
import { loadConfig, resolveConfigPath } from "./config.js";
|
||||||
import { Daemon } from "./daemon.js";
|
import { Daemon } from "./daemon.js";
|
||||||
import { install, uninstall, status } from "./launchd.js";
|
import { install, status, uninstall } from "./launchd.js";
|
||||||
|
import { Logger } from "./logger.js";
|
||||||
|
|
||||||
export const COMMAND_NAME = "sf-server";
|
export const COMMAND_NAME = "sf-server";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SlashCommandBuilder,
|
type REST,
|
||||||
REST,
|
|
||||||
Routes,
|
|
||||||
type RESTPostAPIChatInputApplicationCommandsJSONBody,
|
type RESTPostAPIChatInputApplicationCommandsJSONBody,
|
||||||
|
Routes,
|
||||||
|
SlashCommandBuilder,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import type { ManagedSession } from "./types.js";
|
|
||||||
import type { Logger } from "./logger.js";
|
import type { Logger } from "./logger.js";
|
||||||
|
import type { ManagedSession } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Command definitions
|
// Command definitions
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { homedir } from "node:os";
|
import { homedir } from "node:os";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { parse as parseYaml } from "yaml";
|
import { parse as parseYaml } from "yaml";
|
||||||
|
|
@ -54,7 +54,7 @@ export function validateConfig(raw: unknown): DaemonConfig {
|
||||||
const obj = raw as Record<string, unknown>;
|
const obj = raw as Record<string, unknown>;
|
||||||
|
|
||||||
// --- discord ---
|
// --- discord ---
|
||||||
let discord: DaemonConfig["discord"] = undefined;
|
let discord: DaemonConfig["discord"];
|
||||||
if (obj["discord"] != null && typeof obj["discord"] === "object") {
|
if (obj["discord"] != null && typeof obj["discord"] === "object") {
|
||||||
const d = obj["discord"] as Record<string, unknown>;
|
const d = obj["discord"] as Record<string, unknown>;
|
||||||
discord = {
|
discord = {
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,21 @@
|
||||||
import { describe, it, afterEach, beforeAll, afterAll } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { execFileSync, spawn } from "node:child_process";
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
import {
|
import {
|
||||||
mkdtempSync,
|
|
||||||
writeFileSync,
|
|
||||||
readFileSync,
|
|
||||||
rmSync,
|
|
||||||
existsSync,
|
existsSync,
|
||||||
mkdirSync,
|
mkdirSync,
|
||||||
|
mkdtempSync,
|
||||||
|
readFileSync,
|
||||||
|
rmSync,
|
||||||
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { join } from "node:path";
|
import { homedir, tmpdir } from "node:os";
|
||||||
import { tmpdir, homedir } from "node:os";
|
import { dirname, join } from "node:path";
|
||||||
import { randomUUID } from "node:crypto";
|
|
||||||
import { execFileSync, spawn } from "node:child_process";
|
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { dirname } from "node:path";
|
import { afterAll, afterEach, beforeAll, describe, it } from "vitest";
|
||||||
import { resolveConfigPath, loadConfig, validateConfig } from "./config.js";
|
import { loadConfig, resolveConfigPath, validateConfig } from "./config.js";
|
||||||
import { Logger } from "./logger.js";
|
|
||||||
import { Daemon } from "./daemon.js";
|
import { Daemon } from "./daemon.js";
|
||||||
|
import { Logger } from "./logger.js";
|
||||||
import { SessionManager } from "./session-manager.js";
|
import { SessionManager } from "./session-manager.js";
|
||||||
import type { DaemonConfig, LogEntry } from "./types.js";
|
import type { DaemonConfig, LogEntry } from "./types.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import type { DaemonConfig, ProjectInfo } from "./types.js";
|
|
||||||
import type { Logger } from "./logger.js";
|
|
||||||
import { SessionManager } from "./session-manager.js";
|
|
||||||
import { scanForProjects } from "./project-scanner.js";
|
|
||||||
import { DiscordBot, validateDiscordConfig } from "./discord-bot.js";
|
import { DiscordBot, validateDiscordConfig } from "./discord-bot.js";
|
||||||
import { EventBridge } from "./event-bridge.js";
|
import { EventBridge } from "./event-bridge.js";
|
||||||
|
import type { Logger } from "./logger.js";
|
||||||
import { Orchestrator } from "./orchestrator.js";
|
import { Orchestrator } from "./orchestrator.js";
|
||||||
|
import { scanForProjects } from "./project-scanner.js";
|
||||||
|
import { SessionManager } from "./session-manager.js";
|
||||||
|
import type { DaemonConfig, ProjectInfo } from "./types.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core daemon class — ties config + logger together with lifecycle management.
|
* Core daemon class — ties config + logger together with lifecycle management.
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
import { describe, it, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { mkdtempSync, readFileSync, rmSync, existsSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
import { ChannelType } from "discord.js";
|
import { ChannelType } from "discord.js";
|
||||||
import { isAuthorized, validateDiscordConfig } from "./discord-bot.js";
|
import { afterEach, describe, it } from "vitest";
|
||||||
import { sanitizeChannelName, ChannelManager } from "./channel-manager.js";
|
import { ChannelManager, sanitizeChannelName } from "./channel-manager.js";
|
||||||
import { buildCommands, formatSessionStatus } from "./commands.js";
|
import { buildCommands, formatSessionStatus } from "./commands.js";
|
||||||
import { Daemon } from "./daemon.js";
|
|
||||||
import { Logger } from "./logger.js";
|
|
||||||
import { validateConfig } from "./config.js";
|
import { validateConfig } from "./config.js";
|
||||||
import type { DaemonConfig, LogEntry, ManagedSession } from "./types.js";
|
import { Daemon } from "./daemon.js";
|
||||||
|
import { isAuthorized, validateDiscordConfig } from "./discord-bot.js";
|
||||||
|
import { Logger } from "./logger.js";
|
||||||
|
import type { DaemonConfig, ManagedSession } from "./types.js";
|
||||||
|
|
||||||
// ---------- helpers ----------
|
// ---------- helpers ----------
|
||||||
|
|
||||||
|
|
@ -315,7 +315,7 @@ describe("ChannelManager", () => {
|
||||||
name: string;
|
name: string;
|
||||||
type: number;
|
type: number;
|
||||||
parentId: string | null;
|
parentId: string | null;
|
||||||
edit?: Function;
|
edit?: (editOpts: { parent?: string }) => Promise<unknown>;
|
||||||
}
|
}
|
||||||
>();
|
>();
|
||||||
let createCounter = 0;
|
let createCounter = 0;
|
||||||
|
|
@ -592,7 +592,7 @@ describe("formatSessionStatus", () => {
|
||||||
|
|
||||||
describe("command dispatch", () => {
|
describe("command dispatch", () => {
|
||||||
// Minimal mock of a ChatInputCommandInteraction
|
// Minimal mock of a ChatInputCommandInteraction
|
||||||
function mockInteraction(commandName: string, userId: string = "owner-1") {
|
function _mockInteraction(commandName: string, userId: string = "owner-1") {
|
||||||
let replied = false;
|
let replied = false;
|
||||||
let replyContent = "";
|
let replyContent = "";
|
||||||
|
|
||||||
|
|
@ -611,8 +611,8 @@ describe("command dispatch", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minimal mock of a non-command interaction
|
// Minimal mock of a non-command interaction
|
||||||
function mockNonCommandInteraction(userId: string = "owner-1") {
|
function _mockNonCommandInteraction(userId: string = "owner-1") {
|
||||||
let replied = false;
|
const replied = false;
|
||||||
return {
|
return {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
type: 3, // InteractionType.MessageComponent
|
type: 3, // InteractionType.MessageComponent
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
ActionRowBuilder,
|
||||||
Client,
|
Client,
|
||||||
|
ComponentType,
|
||||||
GatewayIntentBits,
|
GatewayIntentBits,
|
||||||
|
type Interaction,
|
||||||
REST,
|
REST,
|
||||||
StringSelectMenuBuilder,
|
StringSelectMenuBuilder,
|
||||||
ActionRowBuilder,
|
|
||||||
ComponentType,
|
|
||||||
type Interaction,
|
|
||||||
type Guild,
|
|
||||||
type StringSelectMenuInteraction,
|
type StringSelectMenuInteraction,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import type { DaemonConfig, VerbosityLevel, ProjectInfo } from "./types.js";
|
|
||||||
import type { Logger } from "./logger.js";
|
|
||||||
import type { SessionManager } from "./session-manager.js";
|
|
||||||
import { ChannelManager } from "./channel-manager.js";
|
import { ChannelManager } from "./channel-manager.js";
|
||||||
import {
|
import {
|
||||||
buildCommands,
|
buildCommands,
|
||||||
registerGuildCommands,
|
|
||||||
formatSessionStatus,
|
formatSessionStatus,
|
||||||
|
registerGuildCommands,
|
||||||
} from "./commands.js";
|
} from "./commands.js";
|
||||||
import type { EventBridge } from "./event-bridge.js";
|
import type { EventBridge } from "./event-bridge.js";
|
||||||
|
import type { Logger } from "./logger.js";
|
||||||
|
import type { SessionManager } from "./session-manager.js";
|
||||||
|
import type { DaemonConfig, ProjectInfo, VerbosityLevel } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Pure helpers — exported for testability
|
// Pure helpers — exported for testability
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,22 @@
|
||||||
* blocker handling, conversation relay, and cleanup.
|
* blocker handling, conversation relay, and cleanup.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { vi, describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { EventEmitter } from "node:events";
|
import { EventEmitter } from "node:events";
|
||||||
import { EventBridge } from "./event-bridge.js";
|
|
||||||
import type { EventBridgeOptions, BridgeClient } from "./event-bridge.js";
|
|
||||||
import type {
|
import type {
|
||||||
PendingBlocker,
|
|
||||||
ManagedSession,
|
|
||||||
DaemonConfig,
|
|
||||||
SessionStatus,
|
|
||||||
} from "./types.js";
|
|
||||||
import type {
|
|
||||||
SdkAgentEvent,
|
|
||||||
RpcClient,
|
RpcClient,
|
||||||
RpcExtensionUIRequest,
|
RpcExtensionUIRequest,
|
||||||
|
SdkAgentEvent,
|
||||||
} from "@singularity-forge/rpc-client";
|
} from "@singularity-forge/rpc-client";
|
||||||
|
import { describe, it, vi } from "vitest";
|
||||||
|
import type { BridgeClient, EventBridgeOptions } from "./event-bridge.js";
|
||||||
|
import { EventBridge } from "./event-bridge.js";
|
||||||
|
import type {
|
||||||
|
DaemonConfig,
|
||||||
|
ManagedSession,
|
||||||
|
PendingBlocker,
|
||||||
|
SessionStatus,
|
||||||
|
} from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mock factories
|
// Mock factories
|
||||||
|
|
@ -263,7 +263,7 @@ describe("EventBridge", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("filters events based on verbosity", async () => {
|
it("filters events based on verbosity", async () => {
|
||||||
const { bridge, sessionManager, channelManager, logger } = buildBridge();
|
const { bridge, sessionManager, logger } = buildBridge();
|
||||||
bridge.start();
|
bridge.start();
|
||||||
sessionManager.emit("session:started", {
|
sessionManager.emit("session:started", {
|
||||||
sessionId: "sess-1",
|
sessionId: "sess-1",
|
||||||
|
|
@ -470,7 +470,7 @@ describe("EventBridge", () => {
|
||||||
});
|
});
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
const collectorCalls = mockFn(
|
const _collectorCalls = mockFn(
|
||||||
channelManager._channel.createMessageComponentCollector,
|
channelManager._channel.createMessageComponentCollector,
|
||||||
).mock.calls;
|
).mock.calls;
|
||||||
const collector = mockFn(
|
const collector = mockFn(
|
||||||
|
|
@ -518,7 +518,7 @@ describe("EventBridge", () => {
|
||||||
});
|
});
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
const collectorCalls = mockFn(
|
const _collectorCalls = mockFn(
|
||||||
channelManager._channel.createMessageComponentCollector,
|
channelManager._channel.createMessageComponentCollector,
|
||||||
).mock.calls;
|
).mock.calls;
|
||||||
const collector = mockFn(
|
const collector = mockFn(
|
||||||
|
|
|
||||||
|
|
@ -10,28 +10,27 @@
|
||||||
* - DM backup → owner gets DM on blocker when dm_on_blocker configured
|
* - DM backup → owner gets DM on blocker when dm_on_blocker configured
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type {
|
|
||||||
Client,
|
|
||||||
Message,
|
|
||||||
TextChannel,
|
|
||||||
MessageComponentInteraction,
|
|
||||||
} from "discord.js";
|
|
||||||
import { EmbedBuilder, ComponentType } from "discord.js";
|
|
||||||
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
||||||
import type { Logger } from "./logger.js";
|
import type {
|
||||||
import type { DaemonConfig, PendingBlocker } from "./types.js";
|
Message,
|
||||||
import type { SessionManager } from "./session-manager.js";
|
MessageComponentInteraction,
|
||||||
|
TextChannel,
|
||||||
|
} from "discord.js";
|
||||||
|
import { ComponentType, EmbedBuilder } from "discord.js";
|
||||||
import type { ChannelManager } from "./channel-manager.js";
|
import type { ChannelManager } from "./channel-manager.js";
|
||||||
import { MessageBatcher } from "./message-batcher.js";
|
|
||||||
import { VerbosityManager } from "./verbosity.js";
|
|
||||||
import {
|
|
||||||
formatEvent,
|
|
||||||
formatBlocker,
|
|
||||||
formatSessionStarted,
|
|
||||||
formatError,
|
|
||||||
formatCompletion,
|
|
||||||
} from "./event-formatter.js";
|
|
||||||
import { isAuthorized } from "./discord-bot.js";
|
import { isAuthorized } from "./discord-bot.js";
|
||||||
|
import {
|
||||||
|
formatBlocker,
|
||||||
|
formatCompletion,
|
||||||
|
formatError,
|
||||||
|
formatEvent,
|
||||||
|
formatSessionStarted,
|
||||||
|
} from "./event-formatter.js";
|
||||||
|
import type { Logger } from "./logger.js";
|
||||||
|
import { MessageBatcher } from "./message-batcher.js";
|
||||||
|
import type { SessionManager } from "./session-manager.js";
|
||||||
|
import type { DaemonConfig, PendingBlocker } from "./types.js";
|
||||||
|
import { VerbosityManager } from "./verbosity.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,20 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { EmbedBuilder, ActionRowBuilder, ButtonBuilder } from "discord.js";
|
|
||||||
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
||||||
import type { PendingBlocker, FormattedEvent } from "./types.js";
|
import { describe, it } from "vitest";
|
||||||
import type { RpcExtensionUIRequest } from "@singularity-forge/rpc-client";
|
|
||||||
import {
|
import {
|
||||||
formatToolStart,
|
|
||||||
formatToolEnd,
|
|
||||||
formatMessage,
|
|
||||||
formatBlocker,
|
formatBlocker,
|
||||||
formatCompletion,
|
formatCompletion,
|
||||||
formatError,
|
|
||||||
formatCostUpdate,
|
formatCostUpdate,
|
||||||
|
formatError,
|
||||||
|
formatEvent,
|
||||||
|
formatGenericEvent,
|
||||||
|
formatMessage,
|
||||||
formatSessionStarted,
|
formatSessionStarted,
|
||||||
formatTaskTransition,
|
formatTaskTransition,
|
||||||
formatGenericEvent,
|
formatToolEnd,
|
||||||
formatEvent,
|
formatToolStart,
|
||||||
} from "./event-formatter.js";
|
} from "./event-formatter.js";
|
||||||
|
import type { FormattedEvent, PendingBlocker } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,13 @@
|
||||||
* grey = tool / generic
|
* grey = tool / generic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
||||||
import {
|
import {
|
||||||
EmbedBuilder,
|
|
||||||
ActionRowBuilder,
|
ActionRowBuilder,
|
||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
|
EmbedBuilder,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
import type { SdkAgentEvent } from "@singularity-forge/rpc-client";
|
|
||||||
import type { RpcExtensionUIRequest } from "@singularity-forge/rpc-client";
|
|
||||||
import type { FormattedEvent, PendingBlocker } from "./types.js";
|
import type { FormattedEvent, PendingBlocker } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -190,7 +189,7 @@ export function formatBlocker(
|
||||||
const chunks = chunkArray(options.slice(0, 25), 5);
|
const chunks = chunkArray(options.slice(0, 25), 5);
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
const row = new ActionRowBuilder<ButtonBuilder>();
|
const row = new ActionRowBuilder<ButtonBuilder>();
|
||||||
chunk.forEach((opt, i) => {
|
chunk.forEach((opt, _i) => {
|
||||||
const globalIndex = options.indexOf(opt);
|
const globalIndex = options.indexOf(opt);
|
||||||
row.addComponents(
|
row.addComponents(
|
||||||
new ButtonBuilder()
|
new ButtonBuilder()
|
||||||
|
|
@ -414,7 +413,7 @@ export function formatGenericEvent(event: SdkAgentEvent): FormattedEvent {
|
||||||
*/
|
*/
|
||||||
export function formatEvent(
|
export function formatEvent(
|
||||||
event: SdkAgentEvent,
|
event: SdkAgentEvent,
|
||||||
ownerId?: string,
|
_ownerId?: string,
|
||||||
): FormattedEvent {
|
): FormattedEvent {
|
||||||
const type = str(event.type);
|
const type = str(event.type);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,72 +1,72 @@
|
||||||
export type {
|
|
||||||
DaemonConfig,
|
|
||||||
LogLevel,
|
|
||||||
LogEntry,
|
|
||||||
SessionStatus,
|
|
||||||
ManagedSession,
|
|
||||||
PendingBlocker,
|
|
||||||
CostAccumulator,
|
|
||||||
ProjectInfo,
|
|
||||||
ProjectMarker,
|
|
||||||
StartSessionOptions,
|
|
||||||
FormattedEvent,
|
|
||||||
VerbosityLevel,
|
|
||||||
} from "./types.js";
|
|
||||||
export { MAX_EVENTS, INIT_TIMEOUT_MS } from "./types.js";
|
|
||||||
export { resolveConfigPath, loadConfig, validateConfig } from "./config.js";
|
|
||||||
export { Logger } from "./logger.js";
|
|
||||||
export type { LoggerOptions } from "./logger.js";
|
|
||||||
export { Daemon } from "./daemon.js";
|
|
||||||
export { scanForProjects } from "./project-scanner.js";
|
|
||||||
export { SessionManager } from "./session-manager.js";
|
|
||||||
export {
|
|
||||||
DiscordBot,
|
|
||||||
isAuthorized,
|
|
||||||
validateDiscordConfig,
|
|
||||||
} from "./discord-bot.js";
|
|
||||||
export type { DiscordBotOptions } from "./discord-bot.js";
|
|
||||||
export { ChannelManager, sanitizeChannelName } from "./channel-manager.js";
|
|
||||||
export type { ChannelManagerOptions } from "./channel-manager.js";
|
export type { ChannelManagerOptions } from "./channel-manager.js";
|
||||||
|
export { ChannelManager, sanitizeChannelName } from "./channel-manager.js";
|
||||||
export {
|
export {
|
||||||
buildCommands,
|
buildCommands,
|
||||||
formatSessionStatus,
|
formatSessionStatus,
|
||||||
registerGuildCommands,
|
registerGuildCommands,
|
||||||
} from "./commands.js";
|
} from "./commands.js";
|
||||||
export { EventBridge } from "./event-bridge.js";
|
export { loadConfig, resolveConfigPath, validateConfig } from "./config.js";
|
||||||
export type { BridgeClient, EventBridgeOptions } from "./event-bridge.js";
|
export { Daemon } from "./daemon.js";
|
||||||
export { Orchestrator } from "./orchestrator.js";
|
export type { DiscordBotOptions } from "./discord-bot.js";
|
||||||
export type {
|
export {
|
||||||
OrchestratorConfig,
|
DiscordBot,
|
||||||
OrchestratorDeps,
|
isAuthorized,
|
||||||
DiscordMessageLike,
|
validateDiscordConfig,
|
||||||
} from "./orchestrator.js";
|
} from "./discord-bot.js";
|
||||||
export { MessageBatcher } from "./message-batcher.js";
|
export type { BridgeClient, EventBridgeOptions } from "./event-bridge.js";
|
||||||
export type {
|
export { EventBridge } from "./event-bridge.js";
|
||||||
SendPayload,
|
|
||||||
SendFn,
|
|
||||||
BatcherLogger,
|
|
||||||
BatcherOptions,
|
|
||||||
} from "./message-batcher.js";
|
|
||||||
export { VerbosityManager, shouldShowAtLevel } from "./verbosity.js";
|
|
||||||
export {
|
export {
|
||||||
formatToolStart,
|
|
||||||
formatToolEnd,
|
|
||||||
formatMessage,
|
|
||||||
formatBlocker,
|
formatBlocker,
|
||||||
formatCompletion,
|
formatCompletion,
|
||||||
formatError,
|
|
||||||
formatCostUpdate,
|
formatCostUpdate,
|
||||||
|
formatError,
|
||||||
|
formatEvent,
|
||||||
|
formatGenericEvent,
|
||||||
|
formatMessage,
|
||||||
formatSessionStarted,
|
formatSessionStarted,
|
||||||
formatTaskTransition,
|
formatTaskTransition,
|
||||||
formatGenericEvent,
|
formatToolEnd,
|
||||||
formatEvent,
|
formatToolStart,
|
||||||
} from "./event-formatter.js";
|
} from "./event-formatter.js";
|
||||||
|
export type { LaunchdStatus, PlistOptions, RunCommandFn } from "./launchd.js";
|
||||||
export {
|
export {
|
||||||
escapeXml,
|
escapeXml,
|
||||||
generatePlist,
|
generatePlist,
|
||||||
getPlistPath,
|
getPlistPath,
|
||||||
install as installLaunchAgent,
|
install as installLaunchAgent,
|
||||||
uninstall as uninstallLaunchAgent,
|
|
||||||
status as launchAgentStatus,
|
status as launchAgentStatus,
|
||||||
|
uninstall as uninstallLaunchAgent,
|
||||||
} from "./launchd.js";
|
} from "./launchd.js";
|
||||||
export type { PlistOptions, LaunchdStatus, RunCommandFn } from "./launchd.js";
|
export type { LoggerOptions } from "./logger.js";
|
||||||
|
export { Logger } from "./logger.js";
|
||||||
|
export type {
|
||||||
|
BatcherLogger,
|
||||||
|
BatcherOptions,
|
||||||
|
SendFn,
|
||||||
|
SendPayload,
|
||||||
|
} from "./message-batcher.js";
|
||||||
|
export { MessageBatcher } from "./message-batcher.js";
|
||||||
|
export type {
|
||||||
|
DiscordMessageLike,
|
||||||
|
OrchestratorConfig,
|
||||||
|
OrchestratorDeps,
|
||||||
|
} from "./orchestrator.js";
|
||||||
|
export { Orchestrator } from "./orchestrator.js";
|
||||||
|
export { scanForProjects } from "./project-scanner.js";
|
||||||
|
export { SessionManager } from "./session-manager.js";
|
||||||
|
export type {
|
||||||
|
CostAccumulator,
|
||||||
|
DaemonConfig,
|
||||||
|
FormattedEvent,
|
||||||
|
LogEntry,
|
||||||
|
LogLevel,
|
||||||
|
ManagedSession,
|
||||||
|
PendingBlocker,
|
||||||
|
ProjectInfo,
|
||||||
|
ProjectMarker,
|
||||||
|
SessionStatus,
|
||||||
|
StartSessionOptions,
|
||||||
|
VerbosityLevel,
|
||||||
|
} from "./types.js";
|
||||||
|
export { INIT_TIMEOUT_MS, MAX_EVENTS } from "./types.js";
|
||||||
|
export { shouldShowAtLevel, VerbosityManager } from "./verbosity.js";
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,22 @@
|
||||||
import { describe, it, beforeEach, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import {
|
|
||||||
mkdtempSync,
|
|
||||||
existsSync,
|
|
||||||
readFileSync,
|
|
||||||
writeFileSync,
|
|
||||||
rmSync,
|
|
||||||
mkdirSync,
|
|
||||||
statSync,
|
|
||||||
} from "node:fs";
|
|
||||||
import { join, dirname } from "node:path";
|
|
||||||
import { tmpdir, homedir } from "node:os";
|
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
||||||
|
import { homedir, tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterEach, describe, it } from "vitest";
|
||||||
|
import type { PlistOptions, RunCommandFn } from "./launchd.js";
|
||||||
import {
|
import {
|
||||||
escapeXml,
|
escapeXml,
|
||||||
generatePlist,
|
generatePlist,
|
||||||
getPlistPath,
|
getPlistPath,
|
||||||
install,
|
install,
|
||||||
uninstall,
|
|
||||||
status,
|
status,
|
||||||
|
uninstall,
|
||||||
} from "./launchd.js";
|
} from "./launchd.js";
|
||||||
import type { PlistOptions, RunCommandFn, LaunchdStatus } from "./launchd.js";
|
|
||||||
|
|
||||||
// ---------- helpers ----------
|
// ---------- helpers ----------
|
||||||
|
|
||||||
function tmpDir(): string {
|
function _tmpDir(): string {
|
||||||
return mkdtempSync(
|
return mkdtempSync(
|
||||||
join(tmpdir(), `launchd-test-${randomUUID().slice(0, 8)}-`),
|
join(tmpdir(), `launchd-test-${randomUUID().slice(0, 8)}-`),
|
||||||
);
|
);
|
||||||
|
|
@ -184,8 +176,8 @@ describe("getPlistPath", () => {
|
||||||
// ---------- install ----------
|
// ---------- install ----------
|
||||||
|
|
||||||
describe("install", () => {
|
describe("install", () => {
|
||||||
let tmp: string;
|
let _tmp: string;
|
||||||
let fakePlistPath: string;
|
let _fakePlistPath: string;
|
||||||
|
|
||||||
// We can't mock getPlistPath directly, but we can verify the commands
|
// We can't mock getPlistPath directly, but we can verify the commands
|
||||||
// issued and the plist content by intercepting runCommand and filesystem ops.
|
// issued and the plist content by intercepting runCommand and filesystem ops.
|
||||||
|
|
@ -209,7 +201,7 @@ describe("install", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadCalls = calls.filter((c) => c.startsWith("launchctl load"));
|
const loadCalls = calls.filter((c) => c.startsWith("launchctl load"));
|
||||||
const listCalls = calls.filter((c) => c.startsWith("launchctl list"));
|
const _listCalls = calls.filter((c) => c.startsWith("launchctl list"));
|
||||||
// Should have at least attempted launchctl load
|
// Should have at least attempted launchctl load
|
||||||
assert.ok(
|
assert.ok(
|
||||||
loadCalls.length > 0 || calls.length > 0,
|
loadCalls.length > 0 || calls.length > 0,
|
||||||
|
|
@ -243,7 +235,7 @@ describe("install", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The second install should have tried to unload first
|
// The second install should have tried to unload first
|
||||||
const unloadCalls = calls.filter((c) => c.startsWith("launchctl unload"));
|
const _unloadCalls = calls.filter((c) => c.startsWith("launchctl unload"));
|
||||||
// If the plist path exists, we expect at least one unload attempt on second call
|
// If the plist path exists, we expect at least one unload attempt on second call
|
||||||
// This is a command-level check; filesystem existence depends on environment
|
// This is a command-level check; filesystem existence depends on environment
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
import {
|
|
||||||
writeFileSync,
|
|
||||||
unlinkSync,
|
|
||||||
existsSync,
|
|
||||||
chmodSync,
|
|
||||||
mkdirSync,
|
|
||||||
} from "node:fs";
|
|
||||||
import { resolve } from "node:path";
|
|
||||||
import { homedir } from "node:os";
|
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { dirname } from "node:path";
|
import {
|
||||||
|
chmodSync,
|
||||||
|
existsSync,
|
||||||
|
mkdirSync,
|
||||||
|
unlinkSync,
|
||||||
|
writeFileSync,
|
||||||
|
} from "node:fs";
|
||||||
|
import { homedir } from "node:os";
|
||||||
|
import { dirname, resolve } from "node:path";
|
||||||
|
|
||||||
// --------------- types ---------------
|
// --------------- types ---------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { createWriteStream, mkdirSync, type WriteStream } from "node:fs";
|
import { createWriteStream, mkdirSync, type WriteStream } from "node:fs";
|
||||||
import { dirname } from "node:path";
|
import { dirname } from "node:path";
|
||||||
import type { LogLevel, LogEntry } from "./types.js";
|
import type { LogEntry, LogLevel } from "./types.js";
|
||||||
|
|
||||||
const LEVEL_ORDER: Record<LogLevel, number> = {
|
const LEVEL_ORDER: Record<LogLevel, number> = {
|
||||||
debug: 0,
|
debug: 0,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { vi, describe, it, beforeEach, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it, vi } from "vitest";
|
||||||
|
import type { BatcherLogger, SendPayload } from "./message-batcher.js";
|
||||||
import { MessageBatcher } from "./message-batcher.js";
|
import { MessageBatcher } from "./message-batcher.js";
|
||||||
import type { SendPayload, BatcherLogger } from "./message-batcher.js";
|
|
||||||
import type { FormattedEvent } from "./types.js";
|
import type { FormattedEvent } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -265,9 +265,9 @@ describe("MessageBatcher", () => {
|
||||||
|
|
||||||
describe("error handling", () => {
|
describe("error handling", () => {
|
||||||
it("logs error and continues when send throws", async () => {
|
it("logs error and continues when send throws", async () => {
|
||||||
let attempt = 0;
|
let _attempt = 0;
|
||||||
const sendFn = async () => {
|
const sendFn = async () => {
|
||||||
attempt++;
|
_attempt++;
|
||||||
throw new Error("Discord rate limit");
|
throw new Error("Discord rate limit");
|
||||||
};
|
};
|
||||||
const { logger, errors, warns } = createLogger();
|
const { logger, errors, warns } = createLogger();
|
||||||
|
|
|
||||||
|
|
@ -5,25 +5,20 @@
|
||||||
* allowing tool execution and conversation flow testing without real API calls.
|
* allowing tool execution and conversation flow testing without real API calls.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { mkdtempSync, rmSync, existsSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import { existsSync, mkdtempSync, rmSync } from "node:fs";
|
||||||
|
import { tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterEach, describe, it } from "vitest";
|
||||||
|
import { Logger } from "./logger.js";
|
||||||
import {
|
import {
|
||||||
|
type DiscordMessageLike,
|
||||||
Orchestrator,
|
Orchestrator,
|
||||||
type OrchestratorConfig,
|
type OrchestratorConfig,
|
||||||
type OrchestratorDeps,
|
type OrchestratorDeps,
|
||||||
type DiscordMessageLike,
|
|
||||||
} from "./orchestrator.js";
|
} from "./orchestrator.js";
|
||||||
import { Logger } from "./logger.js";
|
import type { ManagedSession, ProjectInfo, SessionStatus } from "./types.js";
|
||||||
import type {
|
|
||||||
ManagedSession,
|
|
||||||
ProjectInfo,
|
|
||||||
SessionStatus,
|
|
||||||
CostAccumulator,
|
|
||||||
} from "./types.js";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
|
||||||
|
|
@ -11,20 +11,20 @@
|
||||||
* at the tool execution layer.
|
* at the tool execution layer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from "zod";
|
|
||||||
import type Anthropic from "@anthropic-ai/sdk";
|
import type Anthropic from "@anthropic-ai/sdk";
|
||||||
import type {
|
import type {
|
||||||
MessageParam,
|
|
||||||
ContentBlockParam,
|
ContentBlockParam,
|
||||||
|
MessageParam,
|
||||||
|
TextBlock,
|
||||||
Tool,
|
Tool,
|
||||||
ToolResultBlockParam,
|
ToolResultBlockParam,
|
||||||
ToolUseBlock,
|
ToolUseBlock,
|
||||||
TextBlock,
|
|
||||||
} from "@anthropic-ai/sdk/resources/messages/messages";
|
} from "@anthropic-ai/sdk/resources/messages/messages";
|
||||||
import type { SessionManager } from "./session-manager.js";
|
import { z } from "zod";
|
||||||
import type { ChannelManager } from "./channel-manager.js";
|
import type { ChannelManager } from "./channel-manager.js";
|
||||||
import type { ProjectInfo, ManagedSession } from "./types.js";
|
|
||||||
import type { Logger } from "./logger.js";
|
import type { Logger } from "./logger.js";
|
||||||
|
import type { SessionManager } from "./session-manager.js";
|
||||||
|
import type { ManagedSession, ProjectInfo } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// API key resolution — requires ANTHROPIC_API_KEY env var
|
// API key resolution — requires ANTHROPIC_API_KEY env var
|
||||||
|
|
|
||||||
|
|
@ -2,19 +2,19 @@
|
||||||
* Tests for the project scanner module.
|
* Tests for the project scanner module.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import {
|
|
||||||
mkdtempSync,
|
|
||||||
mkdirSync,
|
|
||||||
writeFileSync,
|
|
||||||
rmSync,
|
|
||||||
existsSync,
|
|
||||||
chmodSync,
|
|
||||||
} from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir, platform } from "node:os";
|
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
|
import {
|
||||||
|
chmodSync,
|
||||||
|
existsSync,
|
||||||
|
mkdirSync,
|
||||||
|
mkdtempSync,
|
||||||
|
rmSync,
|
||||||
|
writeFileSync,
|
||||||
|
} from "node:fs";
|
||||||
|
import { platform, tmpdir } from "node:os";
|
||||||
|
import { join } from "node:path";
|
||||||
|
import { afterEach, describe, it } from "vitest";
|
||||||
import { scanForProjects } from "./project-scanner.js";
|
import { scanForProjects } from "./project-scanner.js";
|
||||||
|
|
||||||
// ---------- helpers ----------
|
// ---------- helpers ----------
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
* marker files/directories. Reads one level deep (immediate children only).
|
* marker files/directories. Reads one level deep (immediate children only).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { readdir, stat, access } from "node:fs/promises";
|
import { readdir, stat } from "node:fs/promises";
|
||||||
import { join, basename } from "node:path";
|
import { basename, join } from "node:path";
|
||||||
import type { ProjectInfo, ProjectMarker } from "./types.js";
|
import type { ProjectInfo, ProjectMarker } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -6,17 +6,15 @@
|
||||||
* and cleanup without spawning real SF processes.
|
* and cleanup without spawning real SF processes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, beforeEach, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { resolve, basename } from "node:path";
|
import { mkdtempSync, rmSync } from "node:fs";
|
||||||
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { basename, join, resolve } from "node:path";
|
||||||
|
import { afterEach, describe, it } from "vitest";
|
||||||
import { SessionManager } from "./session-manager.js";
|
|
||||||
import { MAX_EVENTS } from "./types.js";
|
|
||||||
import type { ManagedSession, PendingBlocker } from "./types.js";
|
|
||||||
import { Logger } from "./logger.js";
|
import { Logger } from "./logger.js";
|
||||||
|
import { SessionManager } from "./session-manager.js";
|
||||||
|
import type { ManagedSession, PendingBlocker } from "./types.js";
|
||||||
|
import { MAX_EVENTS } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mock RpcClient (duck-typed to match RpcClient interface)
|
// Mock RpcClient (duck-typed to match RpcClient interface)
|
||||||
|
|
@ -843,7 +841,7 @@ describe("SessionManager", () => {
|
||||||
it("logger receives structured calls during lifecycle", async () => {
|
it("logger receives structured calls during lifecycle", async () => {
|
||||||
const { manager, spy } = createManager();
|
const { manager, spy } = createManager();
|
||||||
|
|
||||||
const sessionId = await manager.startSession({
|
const _sessionId = await manager.startSession({
|
||||||
projectDir: "/tmp/log-test",
|
projectDir: "/tmp/log-test",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,23 +14,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { basename, resolve } from "node:path";
|
|
||||||
import { EventEmitter } from "node:events";
|
import { EventEmitter } from "node:events";
|
||||||
import { RpcClient } from "@singularity-forge/rpc-client";
|
import { basename, resolve } from "node:path";
|
||||||
import type {
|
import type {
|
||||||
SdkAgentEvent,
|
|
||||||
RpcInitResult,
|
|
||||||
RpcCostUpdateEvent,
|
RpcCostUpdateEvent,
|
||||||
RpcExtensionUIRequest,
|
RpcExtensionUIRequest,
|
||||||
|
RpcInitResult,
|
||||||
|
SdkAgentEvent,
|
||||||
} from "@singularity-forge/rpc-client";
|
} from "@singularity-forge/rpc-client";
|
||||||
|
import { RpcClient } from "@singularity-forge/rpc-client";
|
||||||
|
import type { Logger } from "./logger.js";
|
||||||
import type {
|
import type {
|
||||||
ManagedSession,
|
ManagedSession,
|
||||||
StartSessionOptions,
|
|
||||||
PendingBlocker,
|
PendingBlocker,
|
||||||
RuntimeHeartbeat,
|
RuntimeHeartbeat,
|
||||||
|
StartSessionOptions,
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
import { MAX_EVENTS, INIT_TIMEOUT_MS } from "./types.js";
|
import { INIT_TIMEOUT_MS, MAX_EVENTS } from "./types.js";
|
||||||
import type { Logger } from "./logger.js";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Inlined detection logic (from headless-events.ts — no internal package imports)
|
// Inlined detection logic (from headless-events.ts — no internal package imports)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import type {
|
import type {
|
||||||
RpcClient,
|
RpcClient,
|
||||||
SdkAgentEvent,
|
|
||||||
RpcExtensionUIRequest,
|
RpcExtensionUIRequest,
|
||||||
|
SdkAgentEvent,
|
||||||
} from "@singularity-forge/rpc-client";
|
} from "@singularity-forge/rpc-client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, it, beforeEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { VerbosityManager, shouldShowAtLevel } from "./verbosity.js";
|
import { beforeEach, describe, it } from "vitest";
|
||||||
|
import { shouldShowAtLevel, VerbosityManager } from "./verbosity.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// VerbosityManager
|
// VerbosityManager
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ const DEFAULT_SHOWN: ReadonlySet<string> = new Set([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/** Event types shown only at verbose level. */
|
/** Event types shown only at verbose level. */
|
||||||
const VERBOSE_ONLY: ReadonlySet<string> = new Set([
|
const _VERBOSE_ONLY: ReadonlySet<string> = new Set([
|
||||||
"cost_update",
|
"cost_update",
|
||||||
"state_update",
|
"state_update",
|
||||||
"status",
|
"status",
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
* Cursor, and other MCP-compatible clients.
|
* Cursor, and other MCP-compatible clients.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SessionManager } from "./session-manager.js";
|
|
||||||
import { createMcpServer } from "./server.js";
|
import { createMcpServer } from "./server.js";
|
||||||
|
import { SessionManager } from "./session-manager.js";
|
||||||
import { loadStoredCredentialEnvKeys } from "./tool-credentials.js";
|
import { loadStoredCredentialEnvKeys } from "./tool-credentials.js";
|
||||||
|
|
||||||
const MCP_PKG = "@modelcontextprotocol/sdk";
|
const MCP_PKG = "@modelcontextprotocol/sdk";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { validateToolArguments } from "../../pi-ai/src/utils/validation.ts";
|
import { validateToolArguments } from "../../pi-ai/src/utils/validation.ts";
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,29 @@
|
||||||
// @singularity-forge/mcp-server — Tests for env-writer utilities
|
// @singularity-forge/mcp-server — Tests for env-writer utilities
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { describe, it, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import {
|
import {
|
||||||
mkdtempSync,
|
|
||||||
mkdirSync,
|
mkdirSync,
|
||||||
rmSync,
|
mkdtempSync,
|
||||||
writeFileSync,
|
|
||||||
readFileSync,
|
readFileSync,
|
||||||
realpathSync,
|
realpathSync,
|
||||||
|
rmSync,
|
||||||
symlinkSync,
|
symlinkSync,
|
||||||
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
import { afterEach, describe, it } from "vitest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
applySecrets,
|
||||||
checkExistingEnvKeys,
|
checkExistingEnvKeys,
|
||||||
detectDestination,
|
detectDestination,
|
||||||
writeEnvKey,
|
|
||||||
applySecrets,
|
|
||||||
isSafeEnvVarKey,
|
isSafeEnvVarKey,
|
||||||
isSupportedDeploymentEnvironment,
|
isSupportedDeploymentEnvironment,
|
||||||
resolveProjectEnvFilePath,
|
resolveProjectEnvFilePath,
|
||||||
shellEscapeSingle,
|
shellEscapeSingle,
|
||||||
|
writeEnvKey,
|
||||||
} from "./env-writer.js";
|
} from "./env-writer.js";
|
||||||
|
|
||||||
function makeTempDir(prefix: string): string {
|
function makeTempDir(prefix: string): string {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@
|
||||||
// destinations, and checking existing keys. Used by secure_env_collect
|
// destinations, and checking existing keys. Used by secure_env_collect
|
||||||
// MCP tool. No TUI dependencies — pure filesystem + process.env operations.
|
// MCP tool. No TUI dependencies — pure filesystem + process.env operations.
|
||||||
|
|
||||||
import { open, readFile, rename, rm } from "node:fs/promises";
|
|
||||||
import {
|
import {
|
||||||
constants,
|
constants,
|
||||||
existsSync,
|
existsSync,
|
||||||
|
|
@ -13,6 +12,7 @@ import {
|
||||||
realpathSync,
|
realpathSync,
|
||||||
statSync,
|
statSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
|
import { open, readFile, rename, rm } from "node:fs/promises";
|
||||||
import {
|
import {
|
||||||
basename,
|
basename,
|
||||||
dirname,
|
dirname,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// SF — Regression tests for importLocalModule candidate resolution (#3954)
|
// SF — Regression tests for importLocalModule candidate resolution (#3954)
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
|
||||||
import { _buildImportCandidates } from "./workflow-tools.js";
|
import { _buildImportCandidates } from "./workflow-tools.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,23 +10,17 @@
|
||||||
* 4. Testing CLI path resolution via static method
|
* 4. Testing CLI path resolution via static method
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, beforeEach, afterEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { EventEmitter } from "node:events";
|
import { afterEach, beforeEach, describe, it } from "vitest";
|
||||||
|
|
||||||
import { SessionManager } from "./session-manager.js";
|
|
||||||
import {
|
import {
|
||||||
buildAskUserQuestionsElicitRequest,
|
buildAskUserQuestionsElicitRequest,
|
||||||
createMcpServer,
|
createMcpServer,
|
||||||
formatAskUserQuestionsElicitResult,
|
formatAskUserQuestionsElicitResult,
|
||||||
} from "./server.js";
|
} from "./server.js";
|
||||||
|
import { SessionManager } from "./session-manager.js";
|
||||||
|
import type { ManagedSession } from "./types.js";
|
||||||
import { MAX_EVENTS } from "./types.js";
|
import { MAX_EVENTS } from "./types.js";
|
||||||
import type {
|
|
||||||
ManagedSession,
|
|
||||||
CostAccumulator,
|
|
||||||
PendingBlocker,
|
|
||||||
} from "./types.js";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mock RpcClient (duck-typed to match RpcClient interface)
|
// Mock RpcClient (duck-typed to match RpcClient interface)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// SF MCP Server — captures reader
|
// SF MCP Server — captures reader
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { resolveSFRoot, resolveRootFile } from "./paths.js";
|
import { resolveRootFile, resolveSFRoot } from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
// SF MCP Server — lightweight structural health checks
|
// SF MCP Server — lightweight structural health checks
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { existsSync, readFileSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import {
|
import {
|
||||||
resolveSFRoot,
|
|
||||||
resolveRootFile,
|
|
||||||
findMilestoneIds,
|
findMilestoneIds,
|
||||||
resolveMilestoneFile,
|
|
||||||
resolveMilestoneDir,
|
|
||||||
findSliceIds,
|
findSliceIds,
|
||||||
resolveSliceFile,
|
|
||||||
findTaskFiles,
|
findTaskFiles,
|
||||||
|
resolveMilestoneDir,
|
||||||
|
resolveMilestoneFile,
|
||||||
|
resolveRootFile,
|
||||||
|
resolveSFRoot,
|
||||||
|
resolveSliceFile,
|
||||||
} from "./paths.js";
|
} from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// SF MCP Server — knowledge base reader
|
// SF MCP Server — knowledge base reader
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { resolveSFRoot, resolveRootFile } from "./paths.js";
|
import { resolveRootFile, resolveSFRoot } from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// SF MCP Server — metrics/history reader
|
// SF MCP Server — metrics/history reader
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import { resolveSFRoot, resolveRootFile } from "./paths.js";
|
import { resolveRootFile, resolveSFRoot } from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Types
|
// Types
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// SF MCP Server — .sf/ directory resolution
|
// SF MCP Server — .sf/ directory resolution
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { existsSync, statSync, readdirSync } from "node:fs";
|
|
||||||
import { join, resolve, dirname, basename } from "node:path";
|
|
||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
||||||
|
import { basename, dirname, join, resolve } from "node:path";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the .sf/ root directory for a project.
|
* Resolve the .sf/ root directory for a project.
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
// SF MCP Server — reader tests
|
// SF MCP Server — reader tests
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { describe, it, beforeAll, afterAll } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { mkdirSync, writeFileSync, rmSync } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import { randomBytes } from "node:crypto";
|
import { randomBytes } from "node:crypto";
|
||||||
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { readProgress } from "./state.js";
|
import { tmpdir } from "node:os";
|
||||||
import { readRoadmap } from "./roadmap.js";
|
import { join } from "node:path";
|
||||||
import { readHistory } from "./metrics.js";
|
import { afterAll, beforeAll, describe, it } from "vitest";
|
||||||
import { readCaptures } from "./captures.js";
|
import { readCaptures } from "./captures.js";
|
||||||
import { readKnowledge } from "./knowledge.js";
|
|
||||||
import { runDoctorLite } from "./doctor-lite.js";
|
import { runDoctorLite } from "./doctor-lite.js";
|
||||||
|
import { readKnowledge } from "./knowledge.js";
|
||||||
|
import { readHistory } from "./metrics.js";
|
||||||
|
import { readRoadmap } from "./roadmap.js";
|
||||||
|
import { readProgress } from "./state.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Test fixture helpers
|
// Test fixture helpers
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
// SF MCP Server — roadmap structure reader
|
// SF MCP Server — roadmap structure reader
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import {
|
import {
|
||||||
resolveSFRoot,
|
|
||||||
findMilestoneIds,
|
findMilestoneIds,
|
||||||
resolveMilestoneFile,
|
|
||||||
findSliceIds,
|
findSliceIds,
|
||||||
resolveSliceFile,
|
|
||||||
findTaskFiles,
|
findTaskFiles,
|
||||||
|
resolveMilestoneFile,
|
||||||
|
resolveSFRoot,
|
||||||
|
resolveSliceFile,
|
||||||
} from "./paths.js";
|
} from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
// SF MCP Server — project state reader
|
// SF MCP Server — project state reader
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
|
|
||||||
import { readFileSync, existsSync } from "node:fs";
|
import { existsSync, readFileSync } from "node:fs";
|
||||||
import {
|
import {
|
||||||
resolveSFRoot,
|
|
||||||
resolveRootFile,
|
|
||||||
findMilestoneIds,
|
findMilestoneIds,
|
||||||
resolveMilestoneDir,
|
|
||||||
resolveMilestoneFile,
|
|
||||||
findSliceIds,
|
findSliceIds,
|
||||||
findTaskFiles,
|
findTaskFiles,
|
||||||
|
resolveRootFile,
|
||||||
|
resolveSFRoot,
|
||||||
} from "./paths.js";
|
} from "./paths.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -2,91 +2,18 @@
|
||||||
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
||||||
//
|
//
|
||||||
// Tests the secure_env_collect tool registered in createMcpServer.
|
// Tests the secure_env_collect tool registered in createMcpServer.
|
||||||
// Uses a mock MCP server to intercept tool registration and elicitInput calls.
|
|
||||||
|
|
||||||
import { describe, it, beforeEach } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import {
|
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||||
mkdtempSync,
|
|
||||||
mkdirSync,
|
|
||||||
rmSync,
|
|
||||||
writeFileSync,
|
|
||||||
readFileSync,
|
|
||||||
} from "node:fs";
|
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
|
||||||
import { createMcpServer } from "./server.js";
|
import { createMcpServer } from "./server.js";
|
||||||
import { SessionManager } from "./session-manager.js";
|
import { SessionManager } from "./session-manager.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Mock infrastructure
|
// Test helpers
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We intercept McpServer construction by monkey-patching the dynamic import.
|
|
||||||
* Instead, we'll test the tool handler indirectly through the exported
|
|
||||||
* createMcpServer function — capturing the registered tool handlers.
|
|
||||||
*
|
|
||||||
* Since createMcpServer dynamically imports McpServer, we need to test at
|
|
||||||
* a level that exercises the tool handler logic. We do this by extracting
|
|
||||||
* the tool handler through the server.tool() calls.
|
|
||||||
*/
|
|
||||||
|
|
||||||
interface RegisteredTool {
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
params: Record<string, unknown>;
|
|
||||||
handler: (args: Record<string, unknown>) => Promise<unknown>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ToolResult {
|
|
||||||
content?: Array<{ type: string; text: string }>;
|
|
||||||
isError?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock McpServer that captures tool registrations and provides
|
|
||||||
* a controllable elicitInput response.
|
|
||||||
*/
|
|
||||||
class MockMcpServer {
|
|
||||||
registeredTools: RegisteredTool[] = [];
|
|
||||||
elicitResponse: { action: string; content?: Record<string, unknown> } = {
|
|
||||||
action: "accept",
|
|
||||||
content: {},
|
|
||||||
};
|
|
||||||
|
|
||||||
server = {
|
|
||||||
elicitInput: async (_params: unknown) => {
|
|
||||||
return this.elicitResponse;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
tool(
|
|
||||||
name: string,
|
|
||||||
description: string,
|
|
||||||
params: Record<string, unknown>,
|
|
||||||
handler: (args: Record<string, unknown>) => Promise<unknown>,
|
|
||||||
) {
|
|
||||||
this.registeredTools.push({ name, description, params, handler });
|
|
||||||
}
|
|
||||||
|
|
||||||
async connect(_transport: unknown) {
|
|
||||||
/* no-op */
|
|
||||||
}
|
|
||||||
async close() {
|
|
||||||
/* no-op */
|
|
||||||
}
|
|
||||||
|
|
||||||
getToolHandler(
|
|
||||||
name: string,
|
|
||||||
): ((args: Record<string, unknown>) => Promise<unknown>) | undefined {
|
|
||||||
return this.registeredTools.find((t) => t.name === name)?.handler;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Helper to create a mock MCP server with secure_env_collect registered
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,19 @@
|
||||||
|
|
||||||
import { execSync } from "node:child_process";
|
import { execSync } from "node:child_process";
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
import { RpcClient } from "@singularity-forge/rpc-client";
|
|
||||||
import type {
|
import type {
|
||||||
SdkAgentEvent,
|
|
||||||
RpcInitResult,
|
|
||||||
RpcCostUpdateEvent,
|
RpcCostUpdateEvent,
|
||||||
RpcExtensionUIRequest,
|
RpcExtensionUIRequest,
|
||||||
|
RpcInitResult,
|
||||||
|
SdkAgentEvent,
|
||||||
} from "@singularity-forge/rpc-client";
|
} from "@singularity-forge/rpc-client";
|
||||||
|
import { RpcClient } from "@singularity-forge/rpc-client";
|
||||||
import type {
|
import type {
|
||||||
ManagedSession,
|
|
||||||
ExecuteOptions,
|
ExecuteOptions,
|
||||||
|
ManagedSession,
|
||||||
PendingBlocker,
|
PendingBlocker,
|
||||||
CostAccumulator,
|
|
||||||
SessionStatus,
|
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
import { MAX_EVENTS, INIT_TIMEOUT_MS } from "./types.js";
|
import { INIT_TIMEOUT_MS, MAX_EVENTS } from "./types.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Inlined detection logic (from headless-events.ts — no internal package imports)
|
// Inlined detection logic (from headless-events.ts — no internal package imports)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
loadStoredCredentialEnvKeys,
|
loadStoredCredentialEnvKeys,
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,8 @@
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
RpcClient,
|
RpcClient,
|
||||||
SdkAgentEvent,
|
|
||||||
RpcCostUpdateEvent,
|
|
||||||
RpcExtensionUIRequest,
|
RpcExtensionUIRequest,
|
||||||
|
SdkAgentEvent,
|
||||||
} from "@singularity-forge/rpc-client";
|
} from "@singularity-forge/rpc-client";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import * as fs from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import * as fs from "node:fs";
|
import { describe, test } from "vitest";
|
||||||
import * as os from "node:os";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import * as fs from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import * as fs from "node:fs";
|
import { describe, test } from "vitest";
|
||||||
import * as os from "node:os";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import * as fs from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import * as fs from "node:fs";
|
import { describe, test } from "vitest";
|
||||||
import * as os from "node:os";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import * as fs from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import * as fs from "node:fs";
|
import { describe, test } from "vitest";
|
||||||
import * as os from "node:os";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { deflateSync } from "node:zlib";
|
import { deflateSync } from "node:zlib";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
* declared "type": "module" and strict ESM resolution was enforced.
|
* declared "type": "module" and strict ESM resolution was enforced.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const pkgPath = path.resolve(__dirname, "..", "..", "package.json");
|
const pkgPath = path.resolve(__dirname, "..", "..", "package.json");
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { spawn } from "node:child_process";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { spawn } from "node:child_process";
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
import { processStreamChunk } from "../stream-process/index.ts";
|
import { processStreamChunk } from "../stream-process/index.ts";
|
||||||
|
|
||||||
describe("processStreamChunk", () => {
|
describe("processStreamChunk", () => {
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,13 @@
|
||||||
* npx vitest run packages/native/src/__tests__/symbol.test.mjs --config vitest.config.ts
|
* npx vitest run packages/native/src/__tests__/symbol.test.mjs --config vitest.config.ts
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import * as fs from "node:fs";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import * as fs from "node:fs";
|
import { describe, test } from "vitest";
|
||||||
import * as os from "node:os";
|
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
@ -147,14 +147,14 @@ describe("native symbol: replaceSymbol()", () => {
|
||||||
|
|
||||||
test("replaces an arrow function declaration", ({ onTestFinished }) => {
|
test("replaces an arrow function declaration", ({ onTestFinished }) => {
|
||||||
const { dir, file } = tempTsFile(
|
const { dir, file } = tempTsFile(
|
||||||
"const greet = (name: string) => { return `Hello ${name}`; }\n",
|
"const greet = (name: string) => { return `Hello " + "$" + "{name}`; }\n",
|
||||||
);
|
);
|
||||||
onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true }));
|
onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true }));
|
||||||
|
|
||||||
const result = native.replaceSymbol(
|
const result = native.replaceSymbol(
|
||||||
file,
|
file,
|
||||||
"greet",
|
"greet",
|
||||||
"const greet = (name: string) => { return `Hi ${name}!`; }",
|
"const greet = (name: string) => { return `Hi " + "$" + "{name}!`; }",
|
||||||
{ fsync: false },
|
{ fsync: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { describe, test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import * as path from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import * as fs from "node:fs";
|
import * as fs from "node:fs";
|
||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { describe, test } from "vitest";
|
||||||
|
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const { watchTree } = await import(
|
const { watchTree } = await import(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { xxHash32, xxHash32Fallback } from "@singularity-forge/native/xxhash";
|
import { xxHash32, xxHash32Fallback } from "@singularity-forge/native/xxhash";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference values computed from the pure-JS xxHash32 implementation
|
* Reference values computed from the pure-JS xxHash32 implementation
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { native } from "../native.js";
|
import { native } from "../native.js";
|
||||||
import type {
|
import type {
|
||||||
|
AstFindMatch,
|
||||||
AstFindOptions,
|
AstFindOptions,
|
||||||
AstFindResult,
|
AstFindResult,
|
||||||
AstReplaceOptions,
|
|
||||||
AstReplaceResult,
|
|
||||||
AstFindMatch,
|
|
||||||
AstReplaceChange,
|
AstReplaceChange,
|
||||||
AstReplaceFileChange,
|
AstReplaceFileChange,
|
||||||
|
AstReplaceOptions,
|
||||||
|
AstReplaceResult,
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|
@ -20,11 +20,13 @@ export type {
|
||||||
};
|
};
|
||||||
|
|
||||||
export function astGrep(options: AstFindOptions): AstFindResult {
|
export function astGrep(options: AstFindOptions): AstFindResult {
|
||||||
return (native as Record<string, Function>).astGrep(options) as AstFindResult;
|
return (native as Record<string, (...args: unknown[]) => unknown>).astGrep(
|
||||||
|
options,
|
||||||
|
) as AstFindResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function astEdit(options: AstReplaceOptions): AstReplaceResult {
|
export function astEdit(options: AstReplaceOptions): AstReplaceResult {
|
||||||
return (native as Record<string, Function>).astEdit(
|
return (native as Record<string, (...args: unknown[]) => unknown>).astEdit(
|
||||||
options,
|
options,
|
||||||
) as AstReplaceResult;
|
) as AstReplaceResult;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,9 @@ export type { DiffResult, FuzzyMatchResult };
|
||||||
* - Special Unicode spaces to regular space
|
* - Special Unicode spaces to regular space
|
||||||
*/
|
*/
|
||||||
export function normalizeForFuzzyMatch(text: string): string {
|
export function normalizeForFuzzyMatch(text: string): string {
|
||||||
return (native as Record<string, Function>).normalizeForFuzzyMatch(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as string;
|
).normalizeForFuzzyMatch(text) as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,10 +33,9 @@ export function fuzzyFindText(
|
||||||
content: string,
|
content: string,
|
||||||
oldText: string,
|
oldText: string,
|
||||||
): FuzzyMatchResult {
|
): FuzzyMatchResult {
|
||||||
return (native as Record<string, Function>).fuzzyFindText(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
oldText,
|
).fuzzyFindText(content, oldText) as FuzzyMatchResult;
|
||||||
) as FuzzyMatchResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,9 +52,7 @@ export function generateDiff(
|
||||||
newContent: string,
|
newContent: string,
|
||||||
contextLines?: number,
|
contextLines?: number,
|
||||||
): DiffResult {
|
): DiffResult {
|
||||||
return (native as Record<string, Function>).generateDiff(
|
return (
|
||||||
oldContent,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
newContent,
|
).generateDiff(oldContent, newContent, contextLines) as DiffResult;
|
||||||
contextLines,
|
|
||||||
) as DiffResult;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
* need to commit them to disk as one native operation.
|
* need to commit them to disk as one native operation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { native } from "../native.js";
|
|
||||||
import { EventEmitter } from "node:events";
|
import { EventEmitter } from "node:events";
|
||||||
|
import { native } from "../native.js";
|
||||||
import type {
|
import type {
|
||||||
ApplyEditsOptions,
|
ApplyEditsOptions,
|
||||||
ApplyEditsResult,
|
ApplyEditsResult,
|
||||||
|
|
|
||||||
|
|
@ -43,9 +43,9 @@ export type {
|
||||||
* of the parsed frontmatter key-value pairs. Parse it with `JSON.parse()`.
|
* of the parsed frontmatter key-value pairs. Parse it with `JSON.parse()`.
|
||||||
*/
|
*/
|
||||||
export function parseFrontmatter(content: string): FrontmatterResult {
|
export function parseFrontmatter(content: string): FrontmatterResult {
|
||||||
return (native as Record<string, Function>).parseFrontmatter(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as FrontmatterResult;
|
).parseFrontmatter(content) as FrontmatterResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -60,11 +60,9 @@ export function extractSection(
|
||||||
heading: string,
|
heading: string,
|
||||||
level?: number,
|
level?: number,
|
||||||
): SectionResult {
|
): SectionResult {
|
||||||
return (native as Record<string, Function>).extractSection(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
heading,
|
).extractSection(content, heading, level) as SectionResult;
|
||||||
level,
|
|
||||||
) as SectionResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,10 +72,9 @@ export function extractSection(
|
||||||
* Parse with `JSON.parse()`.
|
* Parse with `JSON.parse()`.
|
||||||
*/
|
*/
|
||||||
export function extractAllSections(content: string, level?: number): string {
|
export function extractAllSections(content: string, level?: number): string {
|
||||||
return (native as Record<string, Function>).extractAllSections(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
level,
|
).extractAllSections(content, level) as string;
|
||||||
) as string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -87,9 +84,9 @@ export function extractAllSections(content: string, level?: number): string {
|
||||||
* Each file gets frontmatter parsing and section extraction.
|
* Each file gets frontmatter parsing and section extraction.
|
||||||
*/
|
*/
|
||||||
export function batchParseSfFiles(directory: string): BatchParseResult {
|
export function batchParseSfFiles(directory: string): BatchParseResult {
|
||||||
return (native as Record<string, Function>).batchParseSfFiles(
|
return (
|
||||||
directory,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as BatchParseResult;
|
).batchParseSfFiles(directory) as BatchParseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -99,16 +96,16 @@ export function batchParseSfFiles(directory: string): BatchParseResult {
|
||||||
* and boundary map entries.
|
* and boundary map entries.
|
||||||
*/
|
*/
|
||||||
export function parseRoadmapFile(content: string): NativeRoadmap {
|
export function parseRoadmapFile(content: string): NativeRoadmap {
|
||||||
return (native as Record<string, Function>).parseRoadmapFile(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as NativeRoadmap;
|
).parseRoadmapFile(content) as NativeRoadmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scan a `.sf/` directory tree.
|
* Scan a `.sf/` directory tree.
|
||||||
*/
|
*/
|
||||||
export function scanSfTree(directory: string): SfTreeEntry[] {
|
export function scanSfTree(directory: string): SfTreeEntry[] {
|
||||||
return (native as Record<string, Function>).scanSfTree(
|
return (native as Record<string, (...args: unknown[]) => unknown>).scanSfTree(
|
||||||
directory,
|
directory,
|
||||||
) as SfTreeEntry[];
|
) as SfTreeEntry[];
|
||||||
}
|
}
|
||||||
|
|
@ -121,27 +118,25 @@ export function parseJsonlTail(
|
||||||
maxBytes?: number,
|
maxBytes?: number,
|
||||||
maxEntries?: number,
|
maxEntries?: number,
|
||||||
): JsonlParseResult {
|
): JsonlParseResult {
|
||||||
return (native as Record<string, Function>).parseJsonlTail(
|
return (
|
||||||
filePath,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
maxBytes,
|
).parseJsonlTail(filePath, maxBytes, maxEntries) as JsonlParseResult;
|
||||||
maxEntries,
|
|
||||||
) as JsonlParseResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a task plan markdown file into structured data.
|
* Parse a task plan markdown file into structured data.
|
||||||
*/
|
*/
|
||||||
export function parsePlanFile(content: string): NativePlan {
|
export function parsePlanFile(content: string): NativePlan {
|
||||||
return (native as Record<string, Function>).parsePlanFile(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as NativePlan;
|
).parsePlanFile(content) as NativePlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a summary markdown file into structured data.
|
* Parse a summary markdown file into structured data.
|
||||||
*/
|
*/
|
||||||
export function parseSummaryFile(content: string): NativeSummary {
|
export function parseSummaryFile(content: string): NativeSummary {
|
||||||
return (native as Record<string, Function>).parseSummaryFile(
|
return (
|
||||||
content,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as NativeSummary;
|
).parseSummaryFile(content) as NativeSummary;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/** File type classification for filesystem entries. */
|
/** File type classification for filesystem entries. */
|
||||||
export const enum FileType {
|
export enum FileType {
|
||||||
/** Regular file. */
|
/** Regular file. */
|
||||||
File = 1,
|
File = 1,
|
||||||
/** Directory. */
|
/** Directory. */
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ import { native } from "../native.js";
|
||||||
import type { NativeImageHandle } from "./types.js";
|
import type { NativeImageHandle } from "./types.js";
|
||||||
import { ImageFormat, SamplingFilter } from "./types.js";
|
import { ImageFormat, SamplingFilter } from "./types.js";
|
||||||
|
|
||||||
export { ImageFormat, SamplingFilter };
|
|
||||||
export type { NativeImageHandle };
|
export type { NativeImageHandle };
|
||||||
|
export { ImageFormat, SamplingFilter };
|
||||||
|
|
||||||
const NativeImageClass = (native as Record<string, unknown>)
|
const NativeImageClass = (native as Record<string, unknown>)
|
||||||
.NativeImage as NativeImageConstructor;
|
.NativeImage as NativeImageConstructor;
|
||||||
|
|
|
||||||
|
|
@ -14,47 +14,6 @@
|
||||||
for autocomplete and @-mention resolution
|
for autocomplete and @-mention resolution
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {
|
|
||||||
copyToClipboard,
|
|
||||||
readTextFromClipboard,
|
|
||||||
readImageFromClipboard,
|
|
||||||
} from "./clipboard/index.js";
|
|
||||||
export type { ClipboardImage } from "./clipboard/index.js";
|
|
||||||
|
|
||||||
export {
|
|
||||||
highlightCode,
|
|
||||||
supportsLanguage,
|
|
||||||
getSupportedLanguages,
|
|
||||||
} from "./highlight/index.js";
|
|
||||||
export type { HighlightColors } from "./highlight/index.js";
|
|
||||||
|
|
||||||
export { searchContent, grep } from "./grep/index.js";
|
|
||||||
export type {
|
|
||||||
ContextLine,
|
|
||||||
GrepMatch,
|
|
||||||
GrepOptions,
|
|
||||||
GrepResult,
|
|
||||||
SearchMatch,
|
|
||||||
SearchOptions,
|
|
||||||
SearchResult,
|
|
||||||
} from "./grep/index.js";
|
|
||||||
|
|
||||||
export {
|
|
||||||
killTree,
|
|
||||||
listDescendants,
|
|
||||||
processGroupId,
|
|
||||||
killProcessGroup,
|
|
||||||
} from "./ps/index.js";
|
|
||||||
|
|
||||||
export { glob, invalidateFsScanCache } from "./glob/index.js";
|
|
||||||
export type {
|
|
||||||
FileType,
|
|
||||||
GlobMatch,
|
|
||||||
GlobOptions,
|
|
||||||
GlobResult,
|
|
||||||
} from "./glob/index.js";
|
|
||||||
|
|
||||||
export { astGrep, astEdit } from "./ast/index.js";
|
|
||||||
export type {
|
export type {
|
||||||
AstFindMatch,
|
AstFindMatch,
|
||||||
AstFindOptions,
|
AstFindOptions,
|
||||||
|
|
@ -64,35 +23,19 @@ export type {
|
||||||
AstReplaceOptions,
|
AstReplaceOptions,
|
||||||
AstReplaceResult,
|
AstReplaceResult,
|
||||||
} from "./ast/index.js";
|
} from "./ast/index.js";
|
||||||
|
export { astEdit, astGrep } from "./ast/index.js";
|
||||||
export { htmlToMarkdown } from "./html/index.js";
|
export type { ClipboardImage } from "./clipboard/index.js";
|
||||||
export type { HtmlToMarkdownOptions } from "./html/index.js";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
wrapTextWithAnsi,
|
copyToClipboard,
|
||||||
truncateToWidth,
|
readImageFromClipboard,
|
||||||
sliceWithWidth,
|
readTextFromClipboard,
|
||||||
extractSegments,
|
} from "./clipboard/index.js";
|
||||||
sanitizeText,
|
export type { DiffResult, FuzzyMatchResult } from "./diff/index.js";
|
||||||
visibleWidth,
|
|
||||||
EllipsisKind,
|
|
||||||
} from "./text/index.js";
|
|
||||||
export type { SliceResult, ExtractSegmentsResult } from "./text/index.js";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
normalizeForFuzzyMatch,
|
|
||||||
fuzzyFindText,
|
fuzzyFindText,
|
||||||
generateDiff,
|
generateDiff,
|
||||||
|
normalizeForFuzzyMatch,
|
||||||
} from "./diff/index.js";
|
} from "./diff/index.js";
|
||||||
export type { FuzzyMatchResult, DiffResult } from "./diff/index.js";
|
|
||||||
|
|
||||||
export {
|
|
||||||
applyEdits,
|
|
||||||
applyWorkspaceEdit,
|
|
||||||
insertAroundSymbol,
|
|
||||||
replaceSymbol,
|
|
||||||
watchTree,
|
|
||||||
} from "./edit/index.js";
|
|
||||||
export type {
|
export type {
|
||||||
ApplyEditsOptions,
|
ApplyEditsOptions,
|
||||||
ApplyEditsResult,
|
ApplyEditsResult,
|
||||||
|
|
@ -112,49 +55,19 @@ export type {
|
||||||
WatchOptions,
|
WatchOptions,
|
||||||
WorkspaceEditLike,
|
WorkspaceEditLike,
|
||||||
} from "./edit/index.js";
|
} from "./edit/index.js";
|
||||||
|
export {
|
||||||
export { fuzzyFind } from "./fd/index.js";
|
applyEdits,
|
||||||
|
applyWorkspaceEdit,
|
||||||
|
insertAroundSymbol,
|
||||||
|
replaceSymbol,
|
||||||
|
watchTree,
|
||||||
|
} from "./edit/index.js";
|
||||||
export type {
|
export type {
|
||||||
FuzzyFindMatch,
|
FuzzyFindMatch,
|
||||||
FuzzyFindOptions,
|
FuzzyFindOptions,
|
||||||
FuzzyFindResult,
|
FuzzyFindResult,
|
||||||
} from "./fd/index.js";
|
} from "./fd/index.js";
|
||||||
|
export { fuzzyFind } from "./fd/index.js";
|
||||||
export { parseImage, ImageFormat, SamplingFilter } from "./image/index.js";
|
|
||||||
export type { NativeImageHandle } from "./image/index.js";
|
|
||||||
|
|
||||||
export { xxHash32, xxHash32Fallback } from "./xxhash/index.js";
|
|
||||||
|
|
||||||
export {
|
|
||||||
ttsrCompileRules,
|
|
||||||
ttsrCheckBuffer,
|
|
||||||
ttsrFreeRules,
|
|
||||||
} from "./ttsr/index.js";
|
|
||||||
export type { TtsrHandle, TtsrRuleInput } from "./ttsr/index.js";
|
|
||||||
export {
|
|
||||||
parseJson,
|
|
||||||
parsePartialJson,
|
|
||||||
parseStreamingJson,
|
|
||||||
} from "./json-parse/index.js";
|
|
||||||
export {
|
|
||||||
processStreamChunk,
|
|
||||||
stripAnsiNative,
|
|
||||||
sanitizeBinaryOutputNative,
|
|
||||||
} from "./stream-process/index.js";
|
|
||||||
export type { StreamState, StreamChunkResult } from "./stream-process/index.js";
|
|
||||||
|
|
||||||
export {
|
|
||||||
parseFrontmatter,
|
|
||||||
extractSection,
|
|
||||||
extractSection as nativeExtractSection,
|
|
||||||
extractAllSections,
|
|
||||||
batchParseSfFiles,
|
|
||||||
parseRoadmapFile,
|
|
||||||
scanSfTree,
|
|
||||||
parseJsonlTail,
|
|
||||||
parsePlanFile,
|
|
||||||
parseSummaryFile,
|
|
||||||
} from "./forge-parser/index.js";
|
|
||||||
export type {
|
export type {
|
||||||
BatchParseResult,
|
BatchParseResult,
|
||||||
FrontmatterResult,
|
FrontmatterResult,
|
||||||
|
|
@ -172,10 +85,83 @@ export type {
|
||||||
SectionResult,
|
SectionResult,
|
||||||
SfTreeEntry,
|
SfTreeEntry,
|
||||||
} from "./forge-parser/index.js";
|
} from "./forge-parser/index.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
truncateTail,
|
batchParseSfFiles,
|
||||||
|
extractAllSections,
|
||||||
|
extractSection,
|
||||||
|
extractSection as nativeExtractSection,
|
||||||
|
parseFrontmatter,
|
||||||
|
parseJsonlTail,
|
||||||
|
parsePlanFile,
|
||||||
|
parseRoadmapFile,
|
||||||
|
parseSummaryFile,
|
||||||
|
scanSfTree,
|
||||||
|
} from "./forge-parser/index.js";
|
||||||
|
export type {
|
||||||
|
FileType,
|
||||||
|
GlobMatch,
|
||||||
|
GlobOptions,
|
||||||
|
GlobResult,
|
||||||
|
} from "./glob/index.js";
|
||||||
|
export { glob, invalidateFsScanCache } from "./glob/index.js";
|
||||||
|
export type {
|
||||||
|
ContextLine,
|
||||||
|
GrepMatch,
|
||||||
|
GrepOptions,
|
||||||
|
GrepResult,
|
||||||
|
SearchMatch,
|
||||||
|
SearchOptions,
|
||||||
|
SearchResult,
|
||||||
|
} from "./grep/index.js";
|
||||||
|
export { grep, searchContent } from "./grep/index.js";
|
||||||
|
export type { HighlightColors } from "./highlight/index.js";
|
||||||
|
export {
|
||||||
|
getSupportedLanguages,
|
||||||
|
highlightCode,
|
||||||
|
supportsLanguage,
|
||||||
|
} from "./highlight/index.js";
|
||||||
|
export type { HtmlToMarkdownOptions } from "./html/index.js";
|
||||||
|
export { htmlToMarkdown } from "./html/index.js";
|
||||||
|
export type { NativeImageHandle } from "./image/index.js";
|
||||||
|
|
||||||
|
export { ImageFormat, parseImage, SamplingFilter } from "./image/index.js";
|
||||||
|
export {
|
||||||
|
parseJson,
|
||||||
|
parsePartialJson,
|
||||||
|
parseStreamingJson,
|
||||||
|
} from "./json-parse/index.js";
|
||||||
|
export {
|
||||||
|
killProcessGroup,
|
||||||
|
killTree,
|
||||||
|
listDescendants,
|
||||||
|
processGroupId,
|
||||||
|
} from "./ps/index.js";
|
||||||
|
export type { StreamChunkResult, StreamState } from "./stream-process/index.js";
|
||||||
|
export {
|
||||||
|
processStreamChunk,
|
||||||
|
sanitizeBinaryOutputNative,
|
||||||
|
stripAnsiNative,
|
||||||
|
} from "./stream-process/index.js";
|
||||||
|
export type { ExtractSegmentsResult, SliceResult } from "./text/index.js";
|
||||||
|
export {
|
||||||
|
EllipsisKind,
|
||||||
|
extractSegments,
|
||||||
|
sanitizeText,
|
||||||
|
sliceWithWidth,
|
||||||
|
truncateToWidth,
|
||||||
|
visibleWidth,
|
||||||
|
wrapTextWithAnsi,
|
||||||
|
} from "./text/index.js";
|
||||||
|
export type { TruncateOutputResult, TruncateResult } from "./truncate/index.js";
|
||||||
|
export {
|
||||||
truncateHead,
|
truncateHead,
|
||||||
truncateOutput,
|
truncateOutput,
|
||||||
|
truncateTail,
|
||||||
} from "./truncate/index.js";
|
} from "./truncate/index.js";
|
||||||
export type { TruncateResult, TruncateOutputResult } from "./truncate/index.js";
|
export type { TtsrHandle, TtsrRuleInput } from "./ttsr/index.js";
|
||||||
|
export {
|
||||||
|
ttsrCheckBuffer,
|
||||||
|
ttsrCompileRules,
|
||||||
|
ttsrFreeRules,
|
||||||
|
} from "./ttsr/index.js";
|
||||||
|
export { xxHash32, xxHash32Fallback } from "./xxhash/index.js";
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ function loadNative(): Record<string, unknown> {
|
||||||
errors.push(`${devPath}: ${message}`);
|
errors.push(`${devPath}: ${message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const details = errors.map((e) => ` - ${e}`).join("\n");
|
const _details = errors.map((e) => ` - ${e}`).join("\n");
|
||||||
const supportedPlatforms = Object.keys(platformPackageMap);
|
const supportedPlatforms = Object.keys(platformPackageMap);
|
||||||
|
|
||||||
// Graceful fallback: on unsupported platforms (e.g., win32-arm64), return a
|
// Graceful fallback: on unsupported platforms (e.g., win32-arm64), return a
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,9 @@ export function processStreamChunk(
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const result = (native as Record<string, Function>).processStreamChunk(
|
const result = (
|
||||||
chunk,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
napiState,
|
).processStreamChunk(chunk, napiState) as {
|
||||||
) as {
|
|
||||||
text: string;
|
text: string;
|
||||||
state: { utf8Pending: Buffer; ansiPending: Buffer };
|
state: { utf8Pending: Buffer; ansiPending: Buffer };
|
||||||
};
|
};
|
||||||
|
|
@ -59,7 +58,9 @@ export function processStreamChunk(
|
||||||
* Strip ANSI escape sequences from a string.
|
* Strip ANSI escape sequences from a string.
|
||||||
*/
|
*/
|
||||||
export function stripAnsiNative(text: string): string {
|
export function stripAnsiNative(text: string): string {
|
||||||
return (native as Record<string, Function>).stripAnsiNative(text) as string;
|
return (
|
||||||
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
|
).stripAnsiNative(text) as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,7 +70,7 @@ export function stripAnsiNative(text: string): string {
|
||||||
* characters, Unicode format characters (U+FFF9-U+FFFB), and lone surrogates.
|
* characters, Unicode format characters (U+FFF9-U+FFFB), and lone surrogates.
|
||||||
*/
|
*/
|
||||||
export function sanitizeBinaryOutputNative(text: string): string {
|
export function sanitizeBinaryOutputNative(text: string): string {
|
||||||
return (native as Record<string, Function>).sanitizeBinaryOutputNative(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
) as string;
|
).sanitizeBinaryOutputNative(text) as string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@
|
||||||
import { native } from "../native.js";
|
import { native } from "../native.js";
|
||||||
import type { ExtractSegmentsResult, SliceResult } from "./types.js";
|
import type { ExtractSegmentsResult, SliceResult } from "./types.js";
|
||||||
|
|
||||||
export type { ExtractSegmentsResult, SliceResult };
|
|
||||||
export { EllipsisKind } from "./types.js";
|
export { EllipsisKind } from "./types.js";
|
||||||
|
export type { ExtractSegmentsResult, SliceResult };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Word-wrap text to a visible width, preserving ANSI escape codes across
|
* Word-wrap text to a visible width, preserving ANSI escape codes across
|
||||||
|
|
@ -24,11 +24,9 @@ export function wrapTextWithAnsi(
|
||||||
width: number,
|
width: number,
|
||||||
tabWidth?: number,
|
tabWidth?: number,
|
||||||
): string[] {
|
): string[] {
|
||||||
return (native as Record<string, Function>).wrapTextWithAnsi(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
width,
|
).wrapTextWithAnsi(text, width, tabWidth) as string[];
|
||||||
tabWidth,
|
|
||||||
) as string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -47,13 +45,9 @@ export function truncateToWidth(
|
||||||
pad: boolean,
|
pad: boolean,
|
||||||
tabWidth?: number,
|
tabWidth?: number,
|
||||||
): string {
|
): string {
|
||||||
return (native as Record<string, Function>).truncateToWidth(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
maxWidth,
|
).truncateToWidth(text, maxWidth, ellipsisKind, pad, tabWidth) as string;
|
||||||
ellipsisKind,
|
|
||||||
pad,
|
|
||||||
tabWidth,
|
|
||||||
) as string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,13 +63,9 @@ export function sliceWithWidth(
|
||||||
strict: boolean,
|
strict: boolean,
|
||||||
tabWidth?: number,
|
tabWidth?: number,
|
||||||
): SliceResult {
|
): SliceResult {
|
||||||
return (native as Record<string, Function>).sliceWithWidth(
|
return (
|
||||||
line,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
startCol,
|
).sliceWithWidth(line, startCol, length, strict, tabWidth) as SliceResult;
|
||||||
length,
|
|
||||||
strict,
|
|
||||||
tabWidth,
|
|
||||||
) as SliceResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -92,7 +82,9 @@ export function extractSegments(
|
||||||
strictAfter: boolean,
|
strictAfter: boolean,
|
||||||
tabWidth?: number,
|
tabWidth?: number,
|
||||||
): ExtractSegmentsResult {
|
): ExtractSegmentsResult {
|
||||||
return (native as Record<string, Function>).extractSegments(
|
return (
|
||||||
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
|
).extractSegments(
|
||||||
line,
|
line,
|
||||||
beforeEnd,
|
beforeEnd,
|
||||||
afterStart,
|
afterStart,
|
||||||
|
|
@ -109,7 +101,9 @@ export function extractSegments(
|
||||||
* Returns the original string when no changes are needed (zero-copy).
|
* Returns the original string when no changes are needed (zero-copy).
|
||||||
*/
|
*/
|
||||||
export function sanitizeText(text: string): string {
|
export function sanitizeText(text: string): string {
|
||||||
return (native as Record<string, Function>).sanitizeText(text) as string;
|
return (
|
||||||
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
|
).sanitizeText(text) as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -118,8 +112,7 @@ export function sanitizeText(text: string): string {
|
||||||
* Tabs count as `tabWidth` cells (default 3).
|
* Tabs count as `tabWidth` cells (default 3).
|
||||||
*/
|
*/
|
||||||
export function visibleWidth(text: string, tabWidth?: number): number {
|
export function visibleWidth(text: string, tabWidth?: number): number {
|
||||||
return (native as Record<string, Function>).visibleWidth(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
tabWidth,
|
).visibleWidth(text, tabWidth) as number;
|
||||||
) as number;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,20 +24,18 @@ export interface TruncateOutputResult {
|
||||||
* Keep the first `maxBytes` worth of complete lines.
|
* Keep the first `maxBytes` worth of complete lines.
|
||||||
*/
|
*/
|
||||||
export function truncateTail(text: string, maxBytes: number): TruncateResult {
|
export function truncateTail(text: string, maxBytes: number): TruncateResult {
|
||||||
return (native as Record<string, Function>).truncateTail(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
maxBytes,
|
).truncateTail(text, maxBytes) as TruncateResult;
|
||||||
) as TruncateResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep the last `maxBytes` worth of complete lines.
|
* Keep the last `maxBytes` worth of complete lines.
|
||||||
*/
|
*/
|
||||||
export function truncateHead(text: string, maxBytes: number): TruncateResult {
|
export function truncateHead(text: string, maxBytes: number): TruncateResult {
|
||||||
return (native as Record<string, Function>).truncateHead(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
maxBytes,
|
).truncateHead(text, maxBytes) as TruncateResult;
|
||||||
) as TruncateResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -48,9 +46,7 @@ export function truncateOutput(
|
||||||
maxBytes: number,
|
maxBytes: number,
|
||||||
mode?: string,
|
mode?: string,
|
||||||
): TruncateOutputResult {
|
): TruncateOutputResult {
|
||||||
return (native as Record<string, Function>).truncateOutput(
|
return (
|
||||||
text,
|
native as Record<string, (...args: unknown[]) => unknown>
|
||||||
maxBytes,
|
).truncateOutput(text, maxBytes, mode) as TruncateOutputResult;
|
||||||
mode,
|
|
||||||
) as TruncateOutputResult;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@
|
||||||
// and that the footer reads activeInferenceModel instead of state.model.
|
// and that the footer reads activeInferenceModel instead of state.model.
|
||||||
// Regression test for https://github.com/singularity-forge/sf-run/issues/1844 Bug 2
|
// Regression test for https://github.com/singularity-forge/sf-run/issues/1844 Bug 2
|
||||||
|
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { join, dirname } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { Agent } from "./agent.ts";
|
|
||||||
import {
|
import {
|
||||||
getModel,
|
|
||||||
type AssistantMessageEventStream,
|
type AssistantMessageEventStream,
|
||||||
|
getModel,
|
||||||
} from "@singularity-forge/pi-ai";
|
} from "@singularity-forge/pi-ai";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
|
import { Agent } from "./agent.ts";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@ import type {
|
||||||
AgentMessage,
|
AgentMessage,
|
||||||
AgentState,
|
AgentState,
|
||||||
AgentTool,
|
AgentTool,
|
||||||
BeforeToolCallContext,
|
|
||||||
BeforeToolCallResult,
|
|
||||||
AfterToolCallContext,
|
|
||||||
AfterToolCallResult,
|
|
||||||
StreamFn,
|
StreamFn,
|
||||||
ThinkingLevel,
|
ThinkingLevel,
|
||||||
} from "./types.js";
|
} from "./types.js";
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ export interface ProxyStreamOptions extends SimpleStreamOptions {
|
||||||
* });
|
* });
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
function streamProxy(
|
function _streamProxy(
|
||||||
model: Model<any>,
|
model: Model<any>,
|
||||||
context: Context,
|
context: Context,
|
||||||
options: ProxyStreamOptions,
|
options: ProxyStreamOptions,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Static, TSchema } from "@sinclair/typebox";
|
||||||
import type {
|
import type {
|
||||||
AssistantMessage,
|
AssistantMessage,
|
||||||
AssistantMessageEvent,
|
AssistantMessageEvent,
|
||||||
|
|
@ -10,7 +11,6 @@ import type {
|
||||||
Tool,
|
Tool,
|
||||||
ToolResultMessage,
|
ToolResultMessage,
|
||||||
} from "@singularity-forge/pi-ai";
|
} from "@singularity-forge/pi-ai";
|
||||||
import type { Static, TSchema } from "@sinclair/typebox";
|
|
||||||
|
|
||||||
/** Stream function - can return sync or Promise for async config lookup */
|
/** Stream function - can return sync or Promise for async config lookup */
|
||||||
export type StreamFn = (
|
export type StreamFn = (
|
||||||
|
|
@ -255,9 +255,8 @@ export type ThinkingLevel =
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export interface CustomAgentMessages {
|
// biome-ignore lint/suspicious/noEmptyInterface: extension point for downstream declaration merging
|
||||||
// Empty by default - apps extend via declaration merging
|
export interface CustomAgentMessages {}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AgentMessage: Union of LLM messages + custom messages.
|
* AgentMessage: Union of LLM messages + custom messages.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env tsx
|
#!/usr/bin/env tsx
|
||||||
|
|
||||||
import { writeFileSync } from "fs";
|
import { writeFileSync } from "node:fs";
|
||||||
import { join } from "path";
|
import { join } from "node:path";
|
||||||
import { Api, KnownProvider, Model } from "../src/types.js";
|
import type { Api, KnownProvider, Model } from "../src/types.js";
|
||||||
|
|
||||||
const packageRoot = join(import.meta.dirname, "..");
|
const packageRoot = join(import.meta.dirname, "..");
|
||||||
|
|
||||||
|
|
@ -69,7 +69,7 @@ async function fetchOpenRouterModels(): Promise<Model<any>[]> {
|
||||||
if (!model.supported_parameters?.includes("tools")) continue;
|
if (!model.supported_parameters?.includes("tools")) continue;
|
||||||
|
|
||||||
// Parse provider from model ID
|
// Parse provider from model ID
|
||||||
let provider: KnownProvider = "openrouter";
|
const provider: KnownProvider = "openrouter";
|
||||||
let modelKey = model.id;
|
let modelKey = model.id;
|
||||||
|
|
||||||
modelKey = model.id; // Keep full ID for OpenRouter
|
modelKey = model.id; // Keep full ID for OpenRouter
|
||||||
|
|
@ -197,7 +197,7 @@ async function loadModelsDevData(): Promise<Model<any>[]> {
|
||||||
const m = model as ModelsDevModel;
|
const m = model as ModelsDevModel;
|
||||||
if (m.tool_call !== true) continue;
|
if (m.tool_call !== true) continue;
|
||||||
|
|
||||||
let id = modelId;
|
const id = modelId;
|
||||||
|
|
||||||
if (id.startsWith("ai21.jamba")) {
|
if (id.startsWith("ai21.jamba")) {
|
||||||
// These models doesn't support tool use in streaming mode
|
// These models doesn't support tool use in streaming mode
|
||||||
|
|
@ -568,7 +568,7 @@ async function loadModelsDevData(): Promise<Model<any>[]> {
|
||||||
if (m.status === "deprecated") continue;
|
if (m.status === "deprecated") continue;
|
||||||
|
|
||||||
// Claude 4.x models route to Anthropic Messages API
|
// Claude 4.x models route to Anthropic Messages API
|
||||||
const isCopilotClaude4 = /^claude-(haiku|sonnet|opus)-4([.\-]|$)/.test(
|
const isCopilotClaude4 = /^claude-(haiku|sonnet|opus)-4([.-]|$)/.test(
|
||||||
modelId,
|
modelId,
|
||||||
);
|
);
|
||||||
// gpt-5 models require responses API, others use completions
|
// gpt-5 models require responses API, others use completions
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { existsSync, readFileSync, writeFileSync } from "fs";
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
import { createInterface } from "readline";
|
import { createInterface } from "node:readline";
|
||||||
import { getOAuthProvider, getOAuthProviders } from "./utils/oauth/index.js";
|
import { getOAuthProvider, getOAuthProviders } from "./utils/oauth/index.js";
|
||||||
import type { OAuthCredentials, OAuthProviderId } from "./utils/oauth/types.js";
|
import type { OAuthCredentials, OAuthProviderId } from "./utils/oauth/types.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,6 @@ export type {
|
||||||
OAuthProviderInterface,
|
OAuthProviderInterface,
|
||||||
} from "./utils/oauth/types.js";
|
} from "./utils/oauth/types.js";
|
||||||
export * from "./utils/overflow.js";
|
export * from "./utils/overflow.js";
|
||||||
export * from "./utils/typebox-helpers.js";
|
|
||||||
export * from "./utils/repair-tool-json.js";
|
export * from "./utils/repair-tool-json.js";
|
||||||
|
export * from "./utils/typebox-helpers.js";
|
||||||
export * from "./utils/validation.js";
|
export * from "./utils/validation.js";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import { MODELS } from "./models.generated.js";
|
import { MODELS } from "./models.generated.js";
|
||||||
import { getModel, getModels, getProviders } from "./models.js";
|
import { getModel, getModels, getProviders } from "./models.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import {
|
import {
|
||||||
getProviders,
|
|
||||||
getModels,
|
|
||||||
getModel,
|
|
||||||
supportsXhigh,
|
|
||||||
applyCapabilityPatches,
|
applyCapabilityPatches,
|
||||||
|
getModel,
|
||||||
|
getModels,
|
||||||
|
getProviders,
|
||||||
|
supportsXhigh,
|
||||||
} from "./models.js";
|
} from "./models.js";
|
||||||
import type { Api, Model } from "./types.js";
|
import type { Api, Model } from "./types.js";
|
||||||
|
|
||||||
|
|
@ -178,7 +178,7 @@ describe("model registry — kimi-coding provider", () => {
|
||||||
const providers = getProviders();
|
const providers = getProviders();
|
||||||
assert.ok(
|
assert.ok(
|
||||||
providers.includes("kimi-coding"),
|
providers.includes("kimi-coding"),
|
||||||
`Expected \"kimi-coding\" in providers, got: ${providers.join(", ")}`,
|
`Expected "kimi-coding" in providers, got: ${providers.join(", ")}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { MODELS } from "./models.generated.js";
|
|
||||||
import { CUSTOM_MODELS } from "./models.custom.js";
|
import { CUSTOM_MODELS } from "./models.custom.js";
|
||||||
|
import { MODELS } from "./models.generated.js";
|
||||||
import type {
|
import type {
|
||||||
Api,
|
Api,
|
||||||
KnownProvider,
|
KnownProvider,
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,16 @@
|
||||||
* Related: #4392 (opus-4-7 adaptive thinking not recognised on Bedrock)
|
* Related: #4392 (opus-4-7 adaptive thinking not recognised on Bedrock)
|
||||||
* #4352 (pre-existing: only opus-4-6 / sonnet-4-6 whitelisted)
|
* #4352 (pre-existing: only opus-4-6 / sonnet-4-6 whitelisted)
|
||||||
*/
|
*/
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import {
|
|
||||||
supportsAdaptiveThinking,
|
|
||||||
mapThinkingLevelToEffort,
|
|
||||||
buildAdditionalModelRequestFields,
|
|
||||||
type BedrockOptions,
|
|
||||||
} from "./amazon-bedrock.js";
|
|
||||||
|
|
||||||
import type { Model } from "../types.js";
|
import type { Model } from "../types.js";
|
||||||
|
import {
|
||||||
|
type BedrockOptions,
|
||||||
|
buildAdditionalModelRequestFields,
|
||||||
|
mapThinkingLevelToEffort,
|
||||||
|
supportsAdaptiveThinking,
|
||||||
|
} from "./amazon-bedrock.js";
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { test } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import { readFileSync } from "node:fs";
|
import { readFileSync } from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { test } from "vitest";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
usesAnthropicBearerAuth,
|
|
||||||
resolveAnthropicBaseUrl,
|
resolveAnthropicBaseUrl,
|
||||||
|
usesAnthropicBearerAuth,
|
||||||
} from "./anthropic.js";
|
} from "./anthropic.js";
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import { convertTools, mapStopReason } from "./anthropic-shared.js";
|
import { convertTools, mapStopReason } from "./anthropic-shared.js";
|
||||||
|
|
||||||
const makeTool = (name: string) =>
|
const makeTool = (name: string) =>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import type {
|
||||||
|
|
||||||
/** API types that use the Anthropic Messages protocol */
|
/** API types that use the Anthropic Messages protocol */
|
||||||
export type AnthropicApi = "anthropic-messages" | "anthropic-vertex";
|
export type AnthropicApi = "anthropic-messages" | "anthropic-vertex";
|
||||||
|
|
||||||
import type { AssistantMessageEventStream } from "../utils/event-stream.js";
|
import type { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
import { parseAnthropicSSE } from "../utils/event-stream.js";
|
import { parseAnthropicSSE } from "../utils/event-stream.js";
|
||||||
import { parseStreamingJson } from "../utils/json-parse.js";
|
import { parseStreamingJson } from "../utils/json-parse.js";
|
||||||
|
|
@ -233,7 +234,7 @@ export function isTransientNetworkError(error: unknown): boolean {
|
||||||
|
|
||||||
export function extractRetryAfterMs(
|
export function extractRetryAfterMs(
|
||||||
headers: Headers | { get(name: string): string | null },
|
headers: Headers | { get(name: string): string | null },
|
||||||
errorText = "",
|
_errorText = "",
|
||||||
): number | undefined {
|
): number | undefined {
|
||||||
const normalizeDelay = (ms: number): number | undefined =>
|
const normalizeDelay = (ms: number): number | undefined =>
|
||||||
ms > 0 ? Math.ceil(ms + 1000) : undefined;
|
ms > 0 ? Math.ceil(ms + 1000) : undefined;
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,18 @@ import type {
|
||||||
StreamFunction,
|
StreamFunction,
|
||||||
} from "../types.js";
|
} from "../types.js";
|
||||||
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
import {
|
|
||||||
adjustMaxTokensForThinking,
|
|
||||||
buildBaseOptions,
|
|
||||||
isAutoReasoning,
|
|
||||||
resolveReasoningLevel,
|
|
||||||
} from "./simple-options.js";
|
|
||||||
import {
|
import {
|
||||||
type AnthropicOptions,
|
type AnthropicOptions,
|
||||||
mapThinkingLevelToEffort,
|
mapThinkingLevelToEffort,
|
||||||
processAnthropicStream,
|
processAnthropicStream,
|
||||||
supportsAdaptiveThinking,
|
supportsAdaptiveThinking,
|
||||||
} from "./anthropic-shared.js";
|
} from "./anthropic-shared.js";
|
||||||
|
import {
|
||||||
|
adjustMaxTokensForThinking,
|
||||||
|
buildBaseOptions,
|
||||||
|
isAutoReasoning,
|
||||||
|
resolveReasoningLevel,
|
||||||
|
} from "./simple-options.js";
|
||||||
|
|
||||||
let _AnthropicVertexClass: typeof AnthropicVertex | undefined;
|
let _AnthropicVertexClass: typeof AnthropicVertex | undefined;
|
||||||
let _AnthropicSdkClass: typeof Anthropic | undefined;
|
let _AnthropicSdkClass: typeof Anthropic | undefined;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,14 @@ import type {
|
||||||
StreamFunction,
|
StreamFunction,
|
||||||
} from "../types.js";
|
} from "../types.js";
|
||||||
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
|
import {
|
||||||
|
type AnthropicEffort,
|
||||||
|
type AnthropicOptions,
|
||||||
|
extractRetryAfterMs,
|
||||||
|
mapThinkingLevelToEffort,
|
||||||
|
processAnthropicStream,
|
||||||
|
supportsAdaptiveThinking,
|
||||||
|
} from "./anthropic-shared.js";
|
||||||
import {
|
import {
|
||||||
buildCopilotDynamicHeaders,
|
buildCopilotDynamicHeaders,
|
||||||
hasCopilotVisionInput,
|
hasCopilotVisionInput,
|
||||||
|
|
@ -20,14 +27,6 @@ import {
|
||||||
isAutoReasoning,
|
isAutoReasoning,
|
||||||
resolveReasoningLevel,
|
resolveReasoningLevel,
|
||||||
} from "./simple-options.js";
|
} from "./simple-options.js";
|
||||||
import {
|
|
||||||
type AnthropicEffort,
|
|
||||||
type AnthropicOptions,
|
|
||||||
extractRetryAfterMs,
|
|
||||||
mapThinkingLevelToEffort,
|
|
||||||
processAnthropicStream,
|
|
||||||
supportsAdaptiveThinking,
|
|
||||||
} from "./anthropic-shared.js";
|
|
||||||
|
|
||||||
// Re-export types used by other modules
|
// Re-export types used by other modules
|
||||||
export type { AnthropicEffort, AnthropicOptions };
|
export type { AnthropicEffort, AnthropicOptions };
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, it } from "vitest";
|
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { describe, it } from "vitest";
|
||||||
import { sanitizeSchemaForGoogle } from "./google-shared.js";
|
import { sanitizeSchemaForGoogle } from "./google-shared.js";
|
||||||
|
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
// Lazy-loaded: Google GenAI SDK is imported on first use, not at startup.
|
// Lazy-loaded: Google GenAI SDK is imported on first use, not at startup.
|
||||||
// This avoids penalizing users who don't use Google Vertex models.
|
// This avoids penalizing users who don't use Google Vertex models.
|
||||||
import type { GoogleGenAI } from "@google/genai";
|
|
||||||
import type {
|
import type {
|
||||||
GenerateContentConfig,
|
GenerateContentConfig,
|
||||||
GenerateContentParameters,
|
GenerateContentParameters,
|
||||||
|
GoogleGenAI,
|
||||||
ThinkingConfig,
|
ThinkingConfig,
|
||||||
} from "@google/genai";
|
} from "@google/genai";
|
||||||
import { calculateCost } from "../models.js";
|
import { calculateCost } from "../models.js";
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ async function getGoogleGenAIClass(): Promise<typeof GoogleGenAI> {
|
||||||
}
|
}
|
||||||
return _GoogleGenAIClass;
|
return _GoogleGenAIClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
import { getEnvApiKey } from "../env-api-keys.js";
|
import { getEnvApiKey } from "../env-api-keys.js";
|
||||||
import { calculateCost } from "../models.js";
|
import { calculateCost } from "../models.js";
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,16 @@ import type {
|
||||||
} from "../types.js";
|
} from "../types.js";
|
||||||
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
import { parseStreamingJson } from "../utils/json-parse.js";
|
import { parseStreamingJson } from "../utils/json-parse.js";
|
||||||
|
import {
|
||||||
|
type CodexAppServerNotification,
|
||||||
|
getCodexAppServerClient,
|
||||||
|
} from "./codex-app-server-client.js";
|
||||||
import { convertResponsesMessages } from "./openai-responses-shared.js";
|
import { convertResponsesMessages } from "./openai-responses-shared.js";
|
||||||
import {
|
import {
|
||||||
buildBaseOptions,
|
buildBaseOptions,
|
||||||
clampReasoning,
|
clampReasoning,
|
||||||
resolveReasoningLevel,
|
resolveReasoningLevel,
|
||||||
} from "./simple-options.js";
|
} from "./simple-options.js";
|
||||||
import {
|
|
||||||
getCodexAppServerClient,
|
|
||||||
type CodexAppServerNotification,
|
|
||||||
} from "./codex-app-server-client.js";
|
|
||||||
|
|
||||||
export interface OpenAICodexResponsesOptions extends StreamOptions {
|
export interface OpenAICodexResponsesOptions extends StreamOptions {
|
||||||
reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
reasoningEffort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
|
||||||
|
|
@ -657,7 +657,7 @@ function readString(value: unknown): string | undefined {
|
||||||
|
|
||||||
function readErrorMessage(value: unknown): string | undefined {
|
function readErrorMessage(value: unknown): string | undefined {
|
||||||
const object = asObject(value);
|
const object = asObject(value);
|
||||||
const error = asObject(object?.error);
|
const _error = asObject(object?.error);
|
||||||
return readNestedCodexErrorMessage(object) ?? readString(object?.message);
|
return readNestedCodexErrorMessage(object) ?? readString(object?.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ import type { FunctionParameters } from "openai/resources/shared.js";
|
||||||
import { getEnvApiKey } from "../env-api-keys.js";
|
import { getEnvApiKey } from "../env-api-keys.js";
|
||||||
import { calculateCost, supportsXhigh } from "../models.js";
|
import { calculateCost, supportsXhigh } from "../models.js";
|
||||||
import type {
|
import type {
|
||||||
AssistantMessage,
|
|
||||||
Context,
|
Context,
|
||||||
ImageContent,
|
ImageContent,
|
||||||
Message,
|
Message,
|
||||||
|
|
@ -34,12 +33,6 @@ import type {
|
||||||
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
import { parseStreamingJson } from "../utils/json-parse.js";
|
import { parseStreamingJson } from "../utils/json-parse.js";
|
||||||
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
||||||
import { sanitizeToolCallArgumentsForSerialization } from "./sanitize-tool-arguments.js";
|
|
||||||
import {
|
|
||||||
buildBaseOptions,
|
|
||||||
clampReasoning,
|
|
||||||
resolveReasoningLevel,
|
|
||||||
} from "./simple-options.js";
|
|
||||||
import {
|
import {
|
||||||
assertStreamSuccess,
|
assertStreamSuccess,
|
||||||
buildInitialOutput,
|
buildInitialOutput,
|
||||||
|
|
@ -47,6 +40,12 @@ import {
|
||||||
finalizeStream,
|
finalizeStream,
|
||||||
handleStreamError,
|
handleStreamError,
|
||||||
} from "./openai-shared.js";
|
} from "./openai-shared.js";
|
||||||
|
import { sanitizeToolCallArgumentsForSerialization } from "./sanitize-tool-arguments.js";
|
||||||
|
import {
|
||||||
|
buildBaseOptions,
|
||||||
|
clampReasoning,
|
||||||
|
resolveReasoningLevel,
|
||||||
|
} from "./simple-options.js";
|
||||||
import { transformMessagesWithReport } from "./transform-messages.js";
|
import { transformMessagesWithReport } from "./transform-messages.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue