@vitest-agent/engine 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +62 -27
- package/index.js +1 -3
- package/layers/EnvironmentDetectorLive.js +38 -18
- package/layers/ExecutorResolverLive.js +13 -1
- package/layers/LoggerLive.js +37 -22
- package/layers/OutputPipelineLive.js +2 -3
- package/package.json +16 -15
- package/platform.js +3 -2
- package/programs/session-env.js +1 -1
- package/utils/ensure-migrated.js +8 -2
- package/utils/resolve-project-key-from-cwd.js +1 -1
- package/version.js +1 -1
- package/layers/OutputRendererLive.js +0 -23
- package/services/OutputRenderer.js +0 -8
package/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Context, Effect, FileSystem, Layer, LogLevel, Option, Path, Schema } from "effect";
|
|
2
2
|
import { ConfigFileShape } from "@effected/config-file";
|
|
3
|
-
import { Agent, AgentNotFoundError, AgentReport, ArtifactKind, ArtifactKind as ArtifactKind$1, ArtifactSuite, ArtifactSuite as ArtifactSuite$1, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorStatus, CacheManifest, CitedArtifact, CoverageBaselines, CoverageReport, DataStoreError, DetailLevel, DiscoveryError, Environment, Executor, FileCoverageReport,
|
|
3
|
+
import { Agent, AgentNotFoundError, AgentReport, ArtifactKind, ArtifactKind as ArtifactKind$1, ArtifactSuite, ArtifactSuite as ArtifactSuite$1, BehaviorDetail, BehaviorNotFoundError, BehaviorRow, BehaviorStatus, CacheManifest, CitedArtifact, CoverageBaselines, CoverageReport, DataStoreError, DetailLevel, DiscoveryError, Environment, Executor, FileCoverageReport, GoalDetail, GoalNotFoundError, GoalRow, GoalStatus, HistoryRecord, IdempotencyHit, IllegalStatusTransitionError, OutputFormat, Phase, ProjectIdentityNotResolvableError, RegistrationConflictError, ResolvedThresholds, TddTaskAlreadyEndedError, TddTaskNotFoundError, TestClassification, TestRun, TrendEntry, TrendRecord, TurnPayload, VitestAgentConfig } from "@vitest-agent/sdk";
|
|
4
4
|
import { SqlClient } from "effect/sql/SqlClient";
|
|
5
|
+
import { RuntimeEnv } from "@effected/env";
|
|
5
6
|
import { WorkspaceDiscovery, WorkspaceDiscoveryFailure, WorkspaceRootNotFoundError } from "@effected/workspaces";
|
|
6
7
|
import { AppDirs, AppDirsError, XdgEnvError } from "@effected/xdg";
|
|
7
8
|
import { ChildProcessSpawner } from "effect/process";
|
|
@@ -1206,14 +1207,24 @@ export declare class EnvironmentDetector extends EnvironmentDetector_base {}
|
|
|
1206
1207
|
* Pure classification behind {@link EnvironmentDetectorLive}: an agent shell
|
|
1207
1208
|
* wins, then GitHub Actions, then generic CI, else a terminal.
|
|
1208
1209
|
*
|
|
1209
|
-
* @
|
|
1210
|
-
*
|
|
1210
|
+
* @remarks
|
|
1211
|
+
* The agent/CI/human split is `@effected/env`'s `Audience.detect`, so the
|
|
1212
|
+
* engine and every `@effected/cli` front end agree on who is running; this
|
|
1213
|
+
* only refines CI into GitHub Actions versus any other CI.
|
|
1214
|
+
*
|
|
1215
|
+
* @param runtime - the `@effected/env` runtime snapshot to classify
|
|
1211
1216
|
* @public
|
|
1212
1217
|
*/
|
|
1213
|
-
export declare const classifyEnvironment: (
|
|
1218
|
+
export declare const classifyEnvironment: (runtime: RuntimeEnv) => Environment;
|
|
1214
1219
|
/**
|
|
1215
|
-
* Live environment detector
|
|
1216
|
-
* from the injected `env` map
|
|
1220
|
+
* Live environment detector over `@effected/env`'s `CurrentRuntimeEnv`, read
|
|
1221
|
+
* from the injected `env` map only: agent, GitHub Actions and generic CI
|
|
1222
|
+
* detection all come from `env`, never from the ambient process.
|
|
1223
|
+
*
|
|
1224
|
+
* @remarks
|
|
1225
|
+
* The snapshot is taken once, when the layer is built, through
|
|
1226
|
+
* `CurrentRuntimeEnv.layerFrom(env)`: a fresh layer per call and per use, so two
|
|
1227
|
+
* detectors built over different maps in one layer graph each see their own.
|
|
1217
1228
|
*
|
|
1218
1229
|
* @param env - the environment map to consult (the front end passes `process.env`)
|
|
1219
1230
|
* @public
|
|
@@ -1313,15 +1324,35 @@ export declare const HistoryTrackerTest: {
|
|
|
1313
1324
|
//#endregion
|
|
1314
1325
|
//#region src/layers/LoggerLive.d.ts
|
|
1315
1326
|
/**
|
|
1316
|
-
* Create a structured JSON (NDJSON) logger layer for stderr
|
|
1327
|
+
* Create a structured JSON (NDJSON) logger layer for stderr, over
|
|
1328
|
+
* `@effected/cli`'s `CliLog` in its diagnostics-only mode
|
|
1329
|
+
* (`format: "json"`, `plainLogger: false`): the already-resolved level is
|
|
1330
|
+
* passed as `level`, and no plain `CliLogger` is installed beside the sink, so
|
|
1331
|
+
* each record prints once and nothing reads the audience or the terminal.
|
|
1317
1332
|
*
|
|
1318
1333
|
* When level is undefined or `"None"`, installs an empty logger set (silent).
|
|
1319
|
-
* When logFile is set,
|
|
1320
|
-
*
|
|
1321
|
-
*
|
|
1334
|
+
* When logFile is set, `CliLog`'s asynchronous file sink also appends each
|
|
1335
|
+
* NDJSON line (the same shape as the stderr line) to that file; the first
|
|
1336
|
+
* write error prints one stderr line and disables the file sink.
|
|
1337
|
+
*
|
|
1338
|
+
* When `env` is given, it is built into `CurrentRuntimeEnv` for the layer's
|
|
1339
|
+
* own build context, which `CliLog` captures: under GitHub Actions every
|
|
1340
|
+
* stderr record is neutralized (no workflow command at line start, `##[`
|
|
1341
|
+
* escaped), including records logged from a fiber that carries no
|
|
1342
|
+
* `CurrentRuntimeEnv` of its own. Without `env`, records are sanitised but
|
|
1343
|
+
* not neutralized unless the logging fiber provides one.
|
|
1344
|
+
*
|
|
1345
|
+
* @remarks
|
|
1346
|
+
* The file sink needs `FileSystem` and `Path`. `CliLog` leaves them in `R`
|
|
1347
|
+
* (for `file: undefined` too), but they are provided here (Node) so the
|
|
1348
|
+
* public signature stays `Layer.Layer<never>` for every caller.
|
|
1349
|
+
*
|
|
1350
|
+
* @param level - the diagnostics level; see {@link resolveLogLevel}
|
|
1351
|
+
* @param logFile - a file to append NDJSON lines to; see {@link resolveLogFile}
|
|
1352
|
+
* @param env - the environment map to detect the runtime from (the front end passes `process.env`)
|
|
1322
1353
|
* @public
|
|
1323
1354
|
*/
|
|
1324
|
-
export declare const LoggerLive: (level?: LogLevel.LogLevel, logFile?: string) => Layer.Layer<never>;
|
|
1355
|
+
export declare const LoggerLive: (level?: LogLevel.LogLevel, logFile?: string, env?: Readonly<Record<string, string | undefined>>) => Layer.Layer<never>;
|
|
1325
1356
|
/**
|
|
1326
1357
|
* Resolve log level from option or environment variable.
|
|
1327
1358
|
* Priority: explicit option \> `VITEST_REPORTER_LOG_LEVEL` in `env` \> undefined.
|
|
@@ -1344,24 +1375,13 @@ export declare function resolveLogFile(env: Record<string, string | undefined>,
|
|
|
1344
1375
|
//#region src/layers/OutputPipelineLive.d.ts
|
|
1345
1376
|
/**
|
|
1346
1377
|
* The output pipeline: environment detection, executor resolution, format
|
|
1347
|
-
* selection
|
|
1378
|
+
* selection and detail resolution.
|
|
1348
1379
|
*
|
|
1349
1380
|
* @param env - the environment map `EnvironmentDetectorLive` consults
|
|
1350
1381
|
* (the front end passes `process.env`)
|
|
1351
1382
|
* @public
|
|
1352
1383
|
*/
|
|
1353
|
-
export declare const OutputPipelineLive: (env: Record<string, string | undefined>) => Layer.Layer<DetailResolver | EnvironmentDetector | ExecutorResolver | FormatSelector
|
|
1354
|
-
//#endregion
|
|
1355
|
-
//#region src/services/OutputRenderer.d.ts
|
|
1356
|
-
declare const OutputRenderer_base: Context.ServiceClass<OutputRenderer, "vitest-agent/OutputRenderer", {
|
|
1357
|
-
readonly render: (reports: ReadonlyArray<AgentReport>, format: OutputFormat, context: FormatterContext) => Effect.Effect<ReadonlyArray<RenderedOutput>>;
|
|
1358
|
-
}>;
|
|
1359
|
-
/** @public */
|
|
1360
|
-
export declare class OutputRenderer extends OutputRenderer_base {}
|
|
1361
|
-
//#endregion
|
|
1362
|
-
//#region src/layers/OutputRendererLive.d.ts
|
|
1363
|
-
/** @public */
|
|
1364
|
-
export declare const OutputRendererLive: Layer.Layer<OutputRenderer, never, never>;
|
|
1384
|
+
export declare const OutputPipelineLive: (env: Record<string, string | undefined>) => Layer.Layer<DetailResolver | EnvironmentDetector | ExecutorResolver | FormatSelector, never, never>;
|
|
1365
1385
|
//#endregion
|
|
1366
1386
|
//#region src/layers/PathResolutionLive.d.ts
|
|
1367
1387
|
/**
|
|
@@ -1835,18 +1855,27 @@ export interface PlatformOptions {
|
|
|
1835
1855
|
readonly logLevel?: LogLevel.LogLevel | undefined;
|
|
1836
1856
|
/** Optional path for structured NDJSON log output. */
|
|
1837
1857
|
readonly logFile?: string | undefined;
|
|
1858
|
+
/**
|
|
1859
|
+
* Whether `PlatformLive` installs its own logger set (`LoggerLive`, from
|
|
1860
|
+
* `logLevel` / `logFile`). Defaults to `true`. Pass `false` when the caller
|
|
1861
|
+
* already owns the logger set (for example `@effected/cli`'s `CliLog`
|
|
1862
|
+
* installed by `CliRuntime.main`'s `env.log`): `LoggerLive` replaces the
|
|
1863
|
+
* installed loggers, so leaving it in would silence the caller's set.
|
|
1864
|
+
* `logLevel` and `logFile` are ignored when this is `false`.
|
|
1865
|
+
*/
|
|
1866
|
+
readonly logger?: boolean | undefined;
|
|
1838
1867
|
}
|
|
1839
1868
|
/**
|
|
1840
1869
|
* The services {@link PlatformLive} provides.
|
|
1841
1870
|
* @public
|
|
1842
1871
|
*/
|
|
1843
|
-
export type PlatformServices = DataReader | DataStore | ProjectDiscovery | HistoryTracker | EnvironmentDetector | ExecutorResolver | FormatSelector | DetailResolver |
|
|
1872
|
+
export type PlatformServices = DataReader | DataStore | ProjectDiscovery | HistoryTracker | EnvironmentDetector | ExecutorResolver | FormatSelector | DetailResolver | NodeServices.NodeServices | SqliteClient | SqlClient;
|
|
1844
1873
|
/**
|
|
1845
1874
|
* The one platform assembly shared by the CLI, the MCP server and the Vitest
|
|
1846
1875
|
* plugin: SQLite + migrator + Node platform services + Logger, with
|
|
1847
1876
|
* `DataReader`, `DataStore`, `ProjectDiscovery`, `HistoryTracker` and the
|
|
1848
1877
|
* output pipeline (`EnvironmentDetector`, `ExecutorResolver`,
|
|
1849
|
-
* `FormatSelector`, `DetailResolver
|
|
1878
|
+
* `FormatSelector`, `DetailResolver`) built over them.
|
|
1850
1879
|
*
|
|
1851
1880
|
* Every env read goes through `options.env`; the engine never touches
|
|
1852
1881
|
* `process` itself.
|
|
@@ -2583,9 +2612,15 @@ export declare function assembleHistoryRecord(rows: HistoryRow[]): AssembledHist
|
|
|
2583
2612
|
* most once per dbPath in the current process; concurrent calls share the
|
|
2584
2613
|
* same in-flight promise. Subsequent calls (after the first resolves) are
|
|
2585
2614
|
* no-ops.
|
|
2615
|
+
*
|
|
2616
|
+
* @param dbPath - the SQLite file to migrate
|
|
2617
|
+
* @param logLevel - the diagnostics level for the migrator's records; see `LoggerLive`
|
|
2618
|
+
* @param logFile - a file to append NDJSON diagnostics to; see `LoggerLive`
|
|
2619
|
+
* @param env - the environment map `LoggerLive` detects the runtime from, so
|
|
2620
|
+
* records are neutralized under GitHub Actions (the front end passes `process.env`)
|
|
2586
2621
|
* @public
|
|
2587
2622
|
*/
|
|
2588
|
-
export declare function ensureMigrated(dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string): Promise<void>;
|
|
2623
|
+
export declare function ensureMigrated(dbPath: string, logLevel?: LogLevel.LogLevel, logFile?: string, env?: Readonly<Record<string, string | undefined>>): Promise<void>;
|
|
2589
2624
|
/**
|
|
2590
2625
|
* Reset the migration cache. Test-only.
|
|
2591
2626
|
*
|
package/index.js
CHANGED
|
@@ -19,8 +19,6 @@ import { FormatSelectorLive } from "./layers/FormatSelectorLive.js";
|
|
|
19
19
|
import { HistoryTrackerLive } from "./layers/HistoryTrackerLive.js";
|
|
20
20
|
import { HistoryTrackerTest } from "./layers/HistoryTrackerTest.js";
|
|
21
21
|
import { LoggerLive, resolveLogFile, resolveLogLevel } from "./layers/LoggerLive.js";
|
|
22
|
-
import { OutputRenderer } from "./services/OutputRenderer.js";
|
|
23
|
-
import { OutputRendererLive } from "./layers/OutputRendererLive.js";
|
|
24
22
|
import { OutputPipelineLive } from "./layers/OutputPipelineLive.js";
|
|
25
23
|
import { APP_NAMESPACE, DATA_FALLBACK_DIR, PathResolutionLive } from "./layers/PathResolutionLive.js";
|
|
26
24
|
import { PerClientSessionMapReader, PerClientSessionMapWriter } from "./services/PerClientSessionMap.js";
|
|
@@ -60,4 +58,4 @@ import { DATABASE_FILENAME, resolveDataPath } from "./utils/resolve-data-path.js
|
|
|
60
58
|
import { resolveWorkspaceKey } from "./utils/resolve-workspace-key.js";
|
|
61
59
|
import { CURRENT_ENGINE_VERSION } from "./version.js";
|
|
62
60
|
|
|
63
|
-
export { APP_NAMESPACE, AgentContext, CURRENT_ENGINE_VERSION, ConfigLive, DATABASE_FILENAME, DATA_DB_FILENAME, DATA_FALLBACK_DIR, DataReader, DataReaderLive, DataStore, DataStoreLive, DetailResolver, DetailResolverLive, DiscoveryRegistry, DiscoveryRegistryLive, EnvironmentDetector, EnvironmentDetectorLive, EnvironmentDetectorTest, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, HistoryTracker, HistoryTrackerLive, HistoryTrackerTest, IDEMPOTENCY_ROOT_SENTINEL, INLINE_ATTACHMENT_BODY_CAP_BYTES, LoggerLive, NodePlatformLayer, OutputPipelineLive,
|
|
61
|
+
export { APP_NAMESPACE, AgentContext, CURRENT_ENGINE_VERSION, ConfigLive, DATABASE_FILENAME, DATA_DB_FILENAME, DATA_FALLBACK_DIR, DataReader, DataReaderLive, DataStore, DataStoreLive, DetailResolver, DetailResolverLive, DiscoveryRegistry, DiscoveryRegistryLive, EnvironmentDetector, EnvironmentDetectorLive, EnvironmentDetectorTest, ExecutorResolver, ExecutorResolverLive, FormatSelector, FormatSelectorLive, HistoryTracker, HistoryTrackerLive, HistoryTrackerTest, IDEMPOTENCY_ROOT_SENTINEL, INLINE_ATTACHMENT_BODY_CAP_BYTES, LoggerLive, NodePlatformLayer, OutputPipelineLive, PROJECT_MIGRATIONS, PathResolutionLive, PerClientSessionMapReader, PerClientSessionMapReaderLive, PerClientSessionMapWriter, PerClientSessionMapWriterLive, PlatformLive, ProjectDiscovery, ProjectDiscoveryLive, ProjectDiscoveryTest, ProjectIdentity, ProjectIdentityLive, ProjectIdentityTest, REGISTRY_DB_FILENAME, RunContext, RunContext$, RunContextLive, RunContextService, RunContextTest, SESSIONS_DB_FILENAME, SidecarPlatformLive, VitestAgentConfigFile, _resetMigrationCacheForTesting, assembleHistoryRecord, assembleManifest, classifyEnvironment, computeFailureSignature, deriveIdempotencyKey, dispatchRecordTddArtifactEffect, endAgentEffect, ensureMigrated, formatTriageEffect, formatWrapupEffect, historyKey, makeSqliteStack, migration as migration0001, migration$1 as migration0002, normalizeAssertionShape, parseAndValidateTurnPayload, parseSessionEnvExports, recordRunWorkspaceChangesEffect, recordSessionEnd, recordSessionStart, recordTddArtifactByTaskIdEffect, recordTddArtifactEffect, recordTurnEffect, recoverSessionContextFromSessionEnv, registerAgentEffect, migration$2 as registryMigration0001, resolveDataPath, resolveHookPaths, resolveLogFile, resolveLogLevel, resolveProjectDir, resolveProjectIdentityFromCandidates, resolveProjectKeyFromCwd, resolveProjectKeyFromCwdEffect, resolveSessionForRecording, resolveSessionMapPath, resolveWorkspaceKey, migration$3 as sessionMapMigration0001 };
|
|
@@ -1,36 +1,56 @@
|
|
|
1
1
|
import { EnvironmentDetector } from "../services/EnvironmentDetector.js";
|
|
2
|
-
import { Effect, Layer } from "effect";
|
|
3
|
-
import {
|
|
2
|
+
import { Effect, Layer, Option } from "effect";
|
|
3
|
+
import { Audience, CurrentRuntimeEnv } from "@effected/env";
|
|
4
4
|
|
|
5
5
|
//#region src/layers/EnvironmentDetectorLive.ts
|
|
6
|
-
|
|
7
|
-
const
|
|
6
|
+
/** The engine environment for each CI `@effected/env` names; exhaustive over `CiName`. */
|
|
7
|
+
const CI_ENVIRONMENT = {
|
|
8
|
+
"github-actions": "ci-github",
|
|
9
|
+
generic: "ci-generic"
|
|
10
|
+
};
|
|
8
11
|
/**
|
|
9
12
|
* Pure classification behind {@link EnvironmentDetectorLive}: an agent shell
|
|
10
13
|
* wins, then GitHub Actions, then generic CI, else a terminal.
|
|
11
14
|
*
|
|
12
|
-
* @
|
|
13
|
-
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* The agent/CI/human split is `@effected/env`'s `Audience.detect`, so the
|
|
17
|
+
* engine and every `@effected/cli` front end agree on who is running; this
|
|
18
|
+
* only refines CI into GitHub Actions versus any other CI.
|
|
19
|
+
*
|
|
20
|
+
* @param runtime - the `@effected/env` runtime snapshot to classify
|
|
14
21
|
* @public
|
|
15
22
|
*/
|
|
16
|
-
const classifyEnvironment = (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
const classifyEnvironment = (runtime) => {
|
|
24
|
+
switch (Audience.detect(runtime)) {
|
|
25
|
+
case "agent": return "agent-shell";
|
|
26
|
+
case "ci": return Option.match(runtime.ci, {
|
|
27
|
+
onNone: () => "ci-generic",
|
|
28
|
+
onSome: (ci) => CI_ENVIRONMENT[ci]
|
|
29
|
+
});
|
|
30
|
+
case "human": return "terminal";
|
|
31
|
+
}
|
|
21
32
|
};
|
|
22
33
|
/**
|
|
23
|
-
* Live environment detector
|
|
24
|
-
* from the injected `env` map
|
|
34
|
+
* Live environment detector over `@effected/env`'s `CurrentRuntimeEnv`, read
|
|
35
|
+
* from the injected `env` map only: agent, GitHub Actions and generic CI
|
|
36
|
+
* detection all come from `env`, never from the ambient process.
|
|
37
|
+
*
|
|
38
|
+
* @remarks
|
|
39
|
+
* The snapshot is taken once, when the layer is built, through
|
|
40
|
+
* `CurrentRuntimeEnv.layerFrom(env)`: a fresh layer per call and per use, so two
|
|
41
|
+
* detectors built over different maps in one layer graph each see their own.
|
|
25
42
|
*
|
|
26
43
|
* @param env - the environment map to consult (the front end passes `process.env`)
|
|
27
44
|
* @public
|
|
28
45
|
*/
|
|
29
|
-
const EnvironmentDetectorLive = (env) => Layer.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
const EnvironmentDetectorLive = (env) => Layer.effect(EnvironmentDetector, Effect.map(CurrentRuntimeEnv, (runtime) => {
|
|
47
|
+
const environment = classifyEnvironment(runtime);
|
|
48
|
+
return {
|
|
49
|
+
detect: () => Effect.succeed(environment),
|
|
50
|
+
isAgent: Effect.succeed(Option.isSome(runtime.agent)),
|
|
51
|
+
agentName: Effect.succeed(Option.getOrUndefined(runtime.agent))
|
|
52
|
+
};
|
|
53
|
+
})).pipe(Layer.provide(CurrentRuntimeEnv.layerFrom(env)));
|
|
34
54
|
|
|
35
55
|
//#endregion
|
|
36
56
|
export { EnvironmentDetectorLive, classifyEnvironment };
|
|
@@ -2,8 +2,20 @@ import { ExecutorResolver } from "../services/ExecutorResolver.js";
|
|
|
2
2
|
import { Effect, Layer } from "effect";
|
|
3
3
|
|
|
4
4
|
//#region src/layers/ExecutorResolverLive.ts
|
|
5
|
+
/**
|
|
6
|
+
* The `@effected/env` audience each environment implies. An `Executor` is the
|
|
7
|
+
* same three-way split as an `AudienceKind` (`human` / `agent` / `ci`); typing
|
|
8
|
+
* the table as `AudienceKind` and returning it as `Executor` keeps the two
|
|
9
|
+
* unions from drifting apart at compile time.
|
|
10
|
+
*/
|
|
11
|
+
const AUDIENCE_OF = {
|
|
12
|
+
"agent-shell": "agent",
|
|
13
|
+
terminal: "human",
|
|
14
|
+
"ci-github": "ci",
|
|
15
|
+
"ci-generic": "ci"
|
|
16
|
+
};
|
|
5
17
|
/** @public */
|
|
6
|
-
const ExecutorResolverLive = Layer.succeed(ExecutorResolver, { resolve: (env) => Effect.succeed(env
|
|
18
|
+
const ExecutorResolverLive = Layer.succeed(ExecutorResolver, { resolve: (env) => Effect.succeed(AUDIENCE_OF[env]) });
|
|
7
19
|
|
|
8
20
|
//#endregion
|
|
9
21
|
export { ExecutorResolverLive };
|
package/layers/LoggerLive.js
CHANGED
|
@@ -1,33 +1,48 @@
|
|
|
1
|
-
import { Layer, LogLevel, Logger
|
|
2
|
-
import {
|
|
1
|
+
import { Layer, LogLevel, Logger } from "effect";
|
|
2
|
+
import { CurrentRuntimeEnv } from "@effected/env";
|
|
3
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
4
|
+
import * as NodePath from "@effect/platform-node/NodePath";
|
|
5
|
+
import { CliLog } from "@effected/cli";
|
|
3
6
|
|
|
4
7
|
//#region src/layers/LoggerLive.ts
|
|
5
8
|
/**
|
|
6
|
-
* Create a structured JSON (NDJSON) logger layer for stderr
|
|
9
|
+
* Create a structured JSON (NDJSON) logger layer for stderr, over
|
|
10
|
+
* `@effected/cli`'s `CliLog` in its diagnostics-only mode
|
|
11
|
+
* (`format: "json"`, `plainLogger: false`): the already-resolved level is
|
|
12
|
+
* passed as `level`, and no plain `CliLogger` is installed beside the sink, so
|
|
13
|
+
* each record prints once and nothing reads the audience or the terminal.
|
|
7
14
|
*
|
|
8
15
|
* When level is undefined or `"None"`, installs an empty logger set (silent).
|
|
9
|
-
* When logFile is set,
|
|
10
|
-
*
|
|
11
|
-
*
|
|
16
|
+
* When logFile is set, `CliLog`'s asynchronous file sink also appends each
|
|
17
|
+
* NDJSON line (the same shape as the stderr line) to that file; the first
|
|
18
|
+
* write error prints one stderr line and disables the file sink.
|
|
19
|
+
*
|
|
20
|
+
* When `env` is given, it is built into `CurrentRuntimeEnv` for the layer's
|
|
21
|
+
* own build context, which `CliLog` captures: under GitHub Actions every
|
|
22
|
+
* stderr record is neutralized (no workflow command at line start, `##[`
|
|
23
|
+
* escaped), including records logged from a fiber that carries no
|
|
24
|
+
* `CurrentRuntimeEnv` of its own. Without `env`, records are sanitised but
|
|
25
|
+
* not neutralized unless the logging fiber provides one.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* The file sink needs `FileSystem` and `Path`. `CliLog` leaves them in `R`
|
|
29
|
+
* (for `file: undefined` too), but they are provided here (Node) so the
|
|
30
|
+
* public signature stays `Layer.Layer<never>` for every caller.
|
|
31
|
+
*
|
|
32
|
+
* @param level - the diagnostics level; see {@link resolveLogLevel}
|
|
33
|
+
* @param logFile - a file to append NDJSON lines to; see {@link resolveLogFile}
|
|
34
|
+
* @param env - the environment map to detect the runtime from (the front end passes `process.env`)
|
|
12
35
|
* @public
|
|
13
36
|
*/
|
|
14
|
-
const LoggerLive = (level, logFile) => {
|
|
37
|
+
const LoggerLive = (level, logFile, env) => {
|
|
15
38
|
if (!level || level === "None") return Logger.layer([]);
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
try {
|
|
25
|
-
appendFileSync(logFile, `${line}\n`);
|
|
26
|
-
} catch {}
|
|
27
|
-
return line;
|
|
28
|
-
})) : void 0;
|
|
29
|
-
const loggers = fileLogger ? [stderrLogger, fileLogger] : [stderrLogger];
|
|
30
|
-
return Layer.merge(Logger.layer(loggers), Layer.succeed(References.MinimumLogLevel, level));
|
|
39
|
+
const sink = CliLog.layer({
|
|
40
|
+
format: "json",
|
|
41
|
+
plainLogger: false,
|
|
42
|
+
level,
|
|
43
|
+
file: logFile === void 0 ? void 0 : { path: logFile }
|
|
44
|
+
}).pipe(Layer.provide(Layer.merge(NodeFileSystem.layer, NodePath.layer)));
|
|
45
|
+
return env === void 0 ? sink : sink.pipe(Layer.provide(CurrentRuntimeEnv.layerFrom(env)));
|
|
31
46
|
};
|
|
32
47
|
const LEVEL_ALIASES = {
|
|
33
48
|
warn: "Warn",
|
|
@@ -2,19 +2,18 @@ import { DetailResolverLive } from "./DetailResolverLive.js";
|
|
|
2
2
|
import { EnvironmentDetectorLive } from "./EnvironmentDetectorLive.js";
|
|
3
3
|
import { ExecutorResolverLive } from "./ExecutorResolverLive.js";
|
|
4
4
|
import { FormatSelectorLive } from "./FormatSelectorLive.js";
|
|
5
|
-
import { OutputRendererLive } from "./OutputRendererLive.js";
|
|
6
5
|
import { Layer } from "effect";
|
|
7
6
|
|
|
8
7
|
//#region src/layers/OutputPipelineLive.ts
|
|
9
8
|
/**
|
|
10
9
|
* The output pipeline: environment detection, executor resolution, format
|
|
11
|
-
* selection
|
|
10
|
+
* selection and detail resolution.
|
|
12
11
|
*
|
|
13
12
|
* @param env - the environment map `EnvironmentDetectorLive` consults
|
|
14
13
|
* (the front end passes `process.env`)
|
|
15
14
|
* @public
|
|
16
15
|
*/
|
|
17
|
-
const OutputPipelineLive = (env) => Layer.mergeAll(EnvironmentDetectorLive(env), ExecutorResolverLive, FormatSelectorLive, DetailResolverLive
|
|
16
|
+
const OutputPipelineLive = (env) => Layer.mergeAll(EnvironmentDetectorLive(env), ExecutorResolverLive, FormatSelectorLive, DetailResolverLive);
|
|
18
17
|
|
|
19
18
|
//#endregion
|
|
20
19
|
export { OutputPipelineLive };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitest-agent/engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared services, layers, SQLite data layer, migrations and platform resolution for the vitest-agent CLI and MCP server.",
|
|
6
6
|
"keywords": [
|
|
@@ -40,20 +40,21 @@
|
|
|
40
40
|
"./package.json": "./package.json"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@effect/platform-node": "4.0.0
|
|
44
|
-
"@effect/sql-sqlite-node": "4.0.0
|
|
45
|
-
"@effected/
|
|
46
|
-
"@effected/
|
|
47
|
-
"@effected/
|
|
48
|
-
"@effected/
|
|
49
|
-
"@effected/
|
|
50
|
-
"@effected/
|
|
51
|
-
"@effected/
|
|
52
|
-
"@effected/
|
|
53
|
-
"@effected/
|
|
54
|
-
"@
|
|
55
|
-
"
|
|
56
|
-
"
|
|
43
|
+
"@effect/platform-node": "^4.0.0",
|
|
44
|
+
"@effect/sql-sqlite-node": "^4.0.0",
|
|
45
|
+
"@effected/cli": "^0.11.0",
|
|
46
|
+
"@effected/config-file": "^0.14.0",
|
|
47
|
+
"@effected/engine": "^0.3.0",
|
|
48
|
+
"@effected/env": "^0.1.0",
|
|
49
|
+
"@effected/glob": "^0.10.0",
|
|
50
|
+
"@effected/jsonc": "^0.15.0",
|
|
51
|
+
"@effected/toml": "^0.11.0",
|
|
52
|
+
"@effected/walker": "^0.15.0",
|
|
53
|
+
"@effected/workspaces": "^0.31.0",
|
|
54
|
+
"@effected/xdg": "^0.9.0",
|
|
55
|
+
"@effected/yaml": "^0.19.0",
|
|
56
|
+
"@vitest-agent/sdk": "6.0.0",
|
|
57
|
+
"effect": "^4.0.0"
|
|
57
58
|
},
|
|
58
59
|
"engines": {
|
|
59
60
|
"node": ">=24.11.0"
|
package/platform.js
CHANGED
|
@@ -41,7 +41,7 @@ const makeSqliteStack = (filename, migrations = PROJECT_MIGRATIONS) => {
|
|
|
41
41
|
* plugin: SQLite + migrator + Node platform services + Logger, with
|
|
42
42
|
* `DataReader`, `DataStore`, `ProjectDiscovery`, `HistoryTracker` and the
|
|
43
43
|
* output pipeline (`EnvironmentDetector`, `ExecutorResolver`,
|
|
44
|
-
* `FormatSelector`, `DetailResolver
|
|
44
|
+
* `FormatSelector`, `DetailResolver`) built over them.
|
|
45
45
|
*
|
|
46
46
|
* Every env read goes through `options.env`; the engine never touches
|
|
47
47
|
* `process` itself.
|
|
@@ -51,7 +51,8 @@ const makeSqliteStack = (filename, migrations = PROJECT_MIGRATIONS) => {
|
|
|
51
51
|
*/
|
|
52
52
|
const PlatformLive = (options) => {
|
|
53
53
|
const { SqliteLayer, MigratorLayer } = makeSqliteStack(options.dbPath);
|
|
54
|
-
|
|
54
|
+
const platform = Layer.mergeAll(ProjectDiscoveryLive, HistoryTrackerLive, OutputPipelineLive(options.env)).pipe(Layer.provideMerge(DataReaderLive), Layer.provideMerge(DataStoreLive), Layer.provideMerge(MigratorLayer), Layer.provideMerge(SqliteLayer), Layer.provideMerge(NodePlatformLayer));
|
|
55
|
+
return options.logger === false ? platform : platform.pipe(Layer.provideMerge(LoggerLive(options.logLevel, options.logFile, options.env)));
|
|
55
56
|
};
|
|
56
57
|
|
|
57
58
|
//#endregion
|
package/programs/session-env.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
1
|
import { join, resolve } from "node:path";
|
|
2
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
3
3
|
|
|
4
4
|
//#region src/programs/session-env.ts
|
|
5
5
|
const EXPORT_LINE = /^export ([A-Z_][A-Z0-9_]*)=(.*)$/;
|
package/utils/ensure-migrated.js
CHANGED
|
@@ -19,16 +19,22 @@ const getCache = () => {
|
|
|
19
19
|
* most once per dbPath in the current process; concurrent calls share the
|
|
20
20
|
* same in-flight promise. Subsequent calls (after the first resolves) are
|
|
21
21
|
* no-ops.
|
|
22
|
+
*
|
|
23
|
+
* @param dbPath - the SQLite file to migrate
|
|
24
|
+
* @param logLevel - the diagnostics level for the migrator's records; see `LoggerLive`
|
|
25
|
+
* @param logFile - a file to append NDJSON diagnostics to; see `LoggerLive`
|
|
26
|
+
* @param env - the environment map `LoggerLive` detects the runtime from, so
|
|
27
|
+
* records are neutralized under GitHub Actions (the front end passes `process.env`)
|
|
22
28
|
* @public
|
|
23
29
|
*/
|
|
24
|
-
function ensureMigrated(dbPath, logLevel, logFile) {
|
|
30
|
+
function ensureMigrated(dbPath, logLevel, logFile, env) {
|
|
25
31
|
const cache = getCache();
|
|
26
32
|
const cached = cache.get(dbPath);
|
|
27
33
|
if (cached) return cached;
|
|
28
34
|
const { SqliteLayer, MigratorLayer } = makeSqliteStack(dbPath);
|
|
29
35
|
const program = Effect.gen(function* () {
|
|
30
36
|
yield* SqlClient;
|
|
31
|
-
}).pipe(Effect.provide(MigratorLayer), Effect.provide(Layer.merge(SqliteLayer, NodePlatformLayer)), Effect.provide(LoggerLive(logLevel, logFile)));
|
|
37
|
+
}).pipe(Effect.provide(MigratorLayer), Effect.provide(Layer.merge(SqliteLayer, NodePlatformLayer)), Effect.provide(LoggerLive(logLevel, logFile, env)));
|
|
32
38
|
const promise = Effect.runPromise(program);
|
|
33
39
|
cache.set(dbPath, promise);
|
|
34
40
|
promise.catch(() => {});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { gitUrlToProjectKey, normalizeWorkspaceKey } from "@vitest-agent/sdk";
|
|
2
2
|
import { Effect, FileSystem } from "effect";
|
|
3
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
4
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
|
|
6
6
|
//#region src/utils/resolve-project-key-from-cwd.ts
|
|
7
7
|
const PACKAGE_JSON = "package.json";
|
package/version.js
CHANGED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { OutputRenderer } from "../services/OutputRenderer.js";
|
|
2
|
-
import { GfmFormatter, JsonFormatter, MarkdownFormatter, SilentFormatter, TerminalFormatter, ciAnnotationsFormatter } from "@vitest-agent/sdk";
|
|
3
|
-
import { Effect, Layer } from "effect";
|
|
4
|
-
|
|
5
|
-
//#region src/layers/OutputRendererLive.ts
|
|
6
|
-
const formatters = /* @__PURE__ */ new Map([
|
|
7
|
-
["terminal", TerminalFormatter],
|
|
8
|
-
["markdown", MarkdownFormatter],
|
|
9
|
-
["gfm", GfmFormatter],
|
|
10
|
-
["json", JsonFormatter],
|
|
11
|
-
["silent", SilentFormatter],
|
|
12
|
-
["vitest-bypass", SilentFormatter],
|
|
13
|
-
["ci-annotations", ciAnnotationsFormatter]
|
|
14
|
-
]);
|
|
15
|
-
/** @public */
|
|
16
|
-
const OutputRendererLive = Layer.succeed(OutputRenderer, { render: (reports, format, context) => Effect.sync(() => {
|
|
17
|
-
const formatter = formatters.get(format);
|
|
18
|
-
if (!formatter) return [];
|
|
19
|
-
return formatter.render(reports, context);
|
|
20
|
-
}) });
|
|
21
|
-
|
|
22
|
-
//#endregion
|
|
23
|
-
export { OutputRendererLive };
|