mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-01 17:39:39 +02:00
refactor(ts): complete legacy host removal — drop fastify/commander/zod, delete MCP SDK server, remove ManagedRuntime facades
Finishes the remaining EFFECT_NATIVE_REWRITE_PLAN stages in one verified slice: - fastify, @fastify/websocket, commander, zod removed from all package manifests - legacy @modelcontextprotocol/sdk stdio server deleted; effect/unstable/ai McpServer is canonical - no ManagedRuntime or Effect.runPromise program facades remain in production source - gateway server/rpc-contract and client rpc/socket moved onto Effect v4 native http/rpc/socket layers Gates (force-run, no cache): check:tsgo, build, test (96 tests / 11 tasks) all green. Native-class inventory: zero blocking production classes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
a26463afc1
commit
cf12defcd8
30 changed files with 1506 additions and 456 deletions
|
|
@ -12,16 +12,10 @@
|
|||
"test": "bunx --bun vitest run --passWithNoTests --exclude=dist/**"
|
||||
},
|
||||
"dependencies": {
|
||||
"@effect/ai-anthropic": "4.0.0-beta.78",
|
||||
"@effect/ai-openai": "4.0.0-beta.78",
|
||||
"@effect/ai-openrouter": "4.0.0-beta.78",
|
||||
"@effect/atom-react": "4.0.0-beta.78",
|
||||
"@effect/openapi-generator": "4.0.0-beta.78",
|
||||
"@effect/opentelemetry": "4.0.0-beta.78",
|
||||
"@effect/platform-browser": "4.0.0-beta.78",
|
||||
"@effect/platform-bun": "4.0.0-beta.78",
|
||||
"@trustgraph/base": "workspace:*",
|
||||
"@trustgraph/client": "workspace:*",
|
||||
"effect": "4.0.0-beta.78",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
|
|
@ -7,37 +7,77 @@
|
|||
import { Effect } from "effect";
|
||||
import * as Argument from "effect/unstable/cli/Argument";
|
||||
import * as Command from "effect/unstable/cli/Command";
|
||||
import { cliCommandError, withSocket } from "./util.js";
|
||||
import { cliCommandError, withGatewayClient, type CliCommandError } from "./util.js";
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
: {};
|
||||
}
|
||||
|
||||
function stringProperty(source: unknown, key: string): string | undefined {
|
||||
const value = asRecord(source)[key];
|
||||
return typeof value === "string" ? value : undefined;
|
||||
}
|
||||
|
||||
function booleanProperty(source: unknown, key: string): boolean | undefined {
|
||||
const value = asRecord(source)[key];
|
||||
return typeof value === "boolean" ? value : undefined;
|
||||
}
|
||||
|
||||
function responseErrorMessage(source: unknown): string | undefined {
|
||||
const error = asRecord(source).error;
|
||||
if (typeof error === "string") return error;
|
||||
return stringProperty(error, "message");
|
||||
}
|
||||
|
||||
export const agentCommand = Command.make("agent", {
|
||||
question: Argument.string("question").pipe(Argument.withDescription("Question to ask")),
|
||||
}, ({ question }) =>
|
||||
withSocket((socket, opts) =>
|
||||
withGatewayClient((client, opts) =>
|
||||
Effect.gen(function* () {
|
||||
const flow = socket.flow(opts.flow);
|
||||
let streamError: CliCommandError | undefined;
|
||||
|
||||
yield* Effect.callback<void, ReturnType<typeof cliCommandError>>((resume) => {
|
||||
flow.agent(
|
||||
yield* client.runDispatchStream(
|
||||
{
|
||||
scope: "flow",
|
||||
flow: opts.flow,
|
||||
service: "agent",
|
||||
request: {
|
||||
question,
|
||||
(chunk) => {
|
||||
// think — show thought process
|
||||
if (chunk.length > 0) process.stderr.write(chunk);
|
||||
},
|
||||
(chunk) => {
|
||||
// observe — show observations
|
||||
if (chunk.length > 0) process.stderr.write(chunk);
|
||||
},
|
||||
(chunk, complete) => {
|
||||
// answer — print to stdout
|
||||
if (chunk.length > 0) process.stdout.write(chunk);
|
||||
if (complete) {
|
||||
process.stdout.write("\n");
|
||||
resume(Effect.void);
|
||||
}
|
||||
},
|
||||
(err) => resume(Effect.fail(cliCommandError("agent", err))),
|
||||
);
|
||||
});
|
||||
user: opts.user,
|
||||
collection: "default",
|
||||
streaming: true,
|
||||
},
|
||||
},
|
||||
(chunk) => {
|
||||
const resp = asRecord(chunk.response);
|
||||
const chunkType = stringProperty(resp, "chunk_type");
|
||||
const error = chunkType === "error" ? responseErrorMessage(resp) ?? "Unknown agent error" : responseErrorMessage(resp);
|
||||
if (error !== undefined) {
|
||||
streamError = cliCommandError("agent", error);
|
||||
return true;
|
||||
}
|
||||
|
||||
const content = stringProperty(resp, "content") ?? "";
|
||||
const messageComplete = booleanProperty(resp, "end_of_message") === true;
|
||||
const dialogComplete = chunk.complete === true || booleanProperty(resp, "end_of_dialog") === true;
|
||||
|
||||
if (chunkType === "thought" || chunkType === "observation") {
|
||||
if (content.length > 0) process.stderr.write(content);
|
||||
} else if (chunkType === "answer" || chunkType === "final-answer") {
|
||||
if (content.length > 0) process.stdout.write(content);
|
||||
if (messageComplete || dialogComplete) process.stdout.write("\n");
|
||||
}
|
||||
|
||||
return dialogComplete;
|
||||
},
|
||||
{ timeoutMs: 120_000, retries: 2 },
|
||||
);
|
||||
|
||||
if (streamError !== undefined) {
|
||||
return yield* streamError;
|
||||
}
|
||||
}),
|
||||
),
|
||||
).pipe(Command.withDescription("Ask the TrustGraph agent a question"));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@
|
|||
* Shared CLI utilities.
|
||||
*/
|
||||
|
||||
import { createTrustGraphSocket, type BaseApi } from "@trustgraph/client";
|
||||
import {
|
||||
createTrustGraphSocket,
|
||||
makeTrustGraphGatewayClientScoped,
|
||||
type BaseApi,
|
||||
type TrustGraphGatewayClient,
|
||||
} from "@trustgraph/client";
|
||||
import { Duration, Effect } from "effect";
|
||||
import * as O from "effect/Option";
|
||||
import * as S from "effect/Schema";
|
||||
|
|
@ -109,6 +114,23 @@ export function createSocketEffect(opts: CliOpts): Effect.Effect<BaseApi, CliCom
|
|||
);
|
||||
}
|
||||
|
||||
function gatewayUrlWithToken(opts: CliOpts): string {
|
||||
if (opts.token === undefined || opts.token.length === 0) return opts.gateway;
|
||||
const separator = opts.gateway.includes("?") ? "&" : "?";
|
||||
return `${opts.gateway}${separator}token=${encodeURIComponent(opts.token)}`;
|
||||
}
|
||||
|
||||
export const withGatewayClient = Effect.fn("withGatewayClient")(function* <A, E, R>(
|
||||
use: (client: TrustGraphGatewayClient, opts: CliOpts) => Effect.Effect<A, E, R>,
|
||||
) {
|
||||
const opts = yield* getOpts;
|
||||
return yield* Effect.scoped(
|
||||
makeTrustGraphGatewayClientScoped({ url: gatewayUrlWithToken(opts) }).pipe(
|
||||
Effect.flatMap((client) => use(client, opts)),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
export const withSocket = Effect.fn("withSocket")(function* <A, E, R>(
|
||||
use: (socket: BaseApi, opts: CliOpts) => Effect.Effect<A, E, R>,
|
||||
) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue