pi-harness-runtime 1.1.387 → 1.1.388
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/AGENTS.md +9 -4
- package/index.ts +20 -18
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/config-capture/package.json +1 -1
- package/packages/connection-runtime/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/contracts/dist/workspace-env-format.d.ts +71 -0
- package/packages/contracts/dist/workspace-env-format.js +120 -0
- package/packages/contracts/dist/workspace-env.d.ts +7 -30
- package/packages/contracts/dist/workspace-env.js +18 -57
- package/packages/contracts/package.json +1 -1
- package/packages/contracts/src/workspace-env-format.ts +165 -0
- package/packages/contracts/src/workspace-env.ts +27 -99
- package/packages/contracts/test/workspace-env.test.ts +144 -0
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/durable-review/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-sink/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/permission-mode/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/relay/dist/index.d.ts +3 -0
- package/packages/relay/dist/index.js +3 -0
- package/packages/relay/dist/relay-endpoint.d.ts +94 -0
- package/packages/relay/dist/relay-endpoint.js +239 -0
- package/packages/relay/package.json +1 -1
- package/packages/relay/src/index.ts +17 -0
- package/packages/relay/src/relay-endpoint.ts +304 -0
- package/packages/relay/test/relay-endpoint.test.ts +278 -0
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/tui/test/harness-footer.test.ts +1 -1
- package/packages/types/package.json +1 -1
- package/packages/voice-api/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/wiki/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
- package/scripts/relay-client.ts +24 -4
- package/scripts/unified-webhook-server.ts +29 -22
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace environment formatting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Split out of workspace-env.ts so the wording can be unit tested without the
|
|
5
|
+
* node:sqlite dependency (bun 1.3.x has no node:sqlite module).
|
|
6
|
+
*
|
|
7
|
+
* Wording rules:
|
|
8
|
+
* - The mode is only reported when it is EXPLICIT (NODE_ENV). An unknown mode
|
|
9
|
+
* is always shown with its reason, never as a bare word next to a dev
|
|
10
|
+
* server state that could be read as proof of the mode.
|
|
11
|
+
* - A dev/build script is a CAPABILITY, not a state. The status line says
|
|
12
|
+
* "not running (script available)" so it cannot be mistaken for a running
|
|
13
|
+
* server or for evidence about the workspace mode.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Workspace mode with explicit states
|
|
18
|
+
*/
|
|
19
|
+
export type WorkspaceMode = "development" | "production" | "test" | "unknown";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Evidence source for mode detection
|
|
23
|
+
*/
|
|
24
|
+
export interface EnvEvidence {
|
|
25
|
+
source: "process.env" | ".env file" | "package.json" | "command" | "detection";
|
|
26
|
+
key: string;
|
|
27
|
+
value: string;
|
|
28
|
+
timestamp: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Workspace environment result
|
|
33
|
+
*/
|
|
34
|
+
export interface WorkspaceEnvResult {
|
|
35
|
+
workspacePath: string;
|
|
36
|
+
mode: WorkspaceMode;
|
|
37
|
+
evidence: EnvEvidence[];
|
|
38
|
+
hasDevScript: boolean;
|
|
39
|
+
hasBuildScript: boolean;
|
|
40
|
+
packageManager: string | null;
|
|
41
|
+
devServerRunning: boolean;
|
|
42
|
+
buildServerRunning: boolean;
|
|
43
|
+
ramTotalGb: number | null;
|
|
44
|
+
ramAvailableGb: number | null;
|
|
45
|
+
ramEffectiveLimitGb: number | null;
|
|
46
|
+
lowRamWarning: boolean;
|
|
47
|
+
buildAllowed: boolean;
|
|
48
|
+
detectedAt: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Human readable mode label.
|
|
53
|
+
*
|
|
54
|
+
* "unknown" always carries the reason: the mode can only be proven by an
|
|
55
|
+
* explicit NODE_ENV (process env or .env file). When a NODE_ENV IS present
|
|
56
|
+
* but names a mode this detector does not know ("staging"), the label says
|
|
57
|
+
* exactly that instead of claiming there is no NODE_ENV at all.
|
|
58
|
+
*/
|
|
59
|
+
export function describeWorkspaceMode(env: WorkspaceEnvResult): string {
|
|
60
|
+
if (env.mode !== "unknown") {
|
|
61
|
+
return env.mode.toUpperCase();
|
|
62
|
+
}
|
|
63
|
+
const nodeEnv = env.evidence.find(
|
|
64
|
+
(e) => e.key === "NODE_ENV" && (e.source === "process.env" || e.source === ".env file"),
|
|
65
|
+
);
|
|
66
|
+
if (nodeEnv) {
|
|
67
|
+
return `UNKNOWN (NODE_ENV=${nodeEnv.value} is not a known mode)`;
|
|
68
|
+
}
|
|
69
|
+
return "UNKNOWN (no explicit NODE_ENV)";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Describe a dev/build server as a state.
|
|
74
|
+
*
|
|
75
|
+
* "not running (script available)" means the script EXISTS, i.e. a server
|
|
76
|
+
* could be started. It is not evidence that a server is running and not
|
|
77
|
+
* evidence about the workspace mode.
|
|
78
|
+
*/
|
|
79
|
+
export function describeServerState(running: boolean, hasScript: boolean): string {
|
|
80
|
+
if (running) return "running";
|
|
81
|
+
if (hasScript) return "not running (script available)";
|
|
82
|
+
return "no script";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* One line status for the footer / status bar.
|
|
87
|
+
*/
|
|
88
|
+
export function formatEnvStatusLine(env: WorkspaceEnvResult): string {
|
|
89
|
+
const mode = describeWorkspaceMode(env);
|
|
90
|
+
const devStatus = describeServerState(env.devServerRunning, env.hasDevScript);
|
|
91
|
+
const buildStatus = describeServerState(env.buildServerRunning, env.hasBuildScript);
|
|
92
|
+
|
|
93
|
+
let status = `Workspace: ${mode} | dev server: ${devStatus} | build: ${buildStatus}`;
|
|
94
|
+
const ramStr = formatRam(env);
|
|
95
|
+
if (ramStr) {
|
|
96
|
+
status += ` | RAM: ${ramStr}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return status;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* RAM segment of the status line.
|
|
104
|
+
*
|
|
105
|
+
* Both values are optional - an unreadable /proc must never produce a
|
|
106
|
+
* "7.7/null" line.
|
|
107
|
+
*/
|
|
108
|
+
function formatRam(env: WorkspaceEnvResult): string {
|
|
109
|
+
const total = env.ramTotalGb;
|
|
110
|
+
const available = env.ramAvailableGb;
|
|
111
|
+
if (total === null && available === null) return "";
|
|
112
|
+
if (total === null) return `${available}GB available`;
|
|
113
|
+
if (available === null) return `${total}GB total`;
|
|
114
|
+
return `${available}/${total}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* RAM segment for the agent block.
|
|
119
|
+
*/
|
|
120
|
+
function formatRamForAgent(env: WorkspaceEnvResult): string {
|
|
121
|
+
const total = env.ramTotalGb;
|
|
122
|
+
const available = env.ramAvailableGb;
|
|
123
|
+
if (total !== null && available !== null) return `${available}/${total}GB available`;
|
|
124
|
+
if (total !== null) return `${total}GB total`;
|
|
125
|
+
return `${available}GB available`;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Multi line description for the agent context.
|
|
130
|
+
*/
|
|
131
|
+
export function formatEnvForAgentResult(env: WorkspaceEnvResult): string {
|
|
132
|
+
let message = `[Workspace Environment]\n`;
|
|
133
|
+
message += `Mode: ${describeWorkspaceMode(env)}\n`;
|
|
134
|
+
|
|
135
|
+
if (env.evidence.length > 0) {
|
|
136
|
+
const sources = env.evidence.map((e) => `${e.source}:${e.value}`).join(", ");
|
|
137
|
+
message += `Evidence: ${sources}\n`;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
message += `Dev script: ${env.hasDevScript ? "yes" : "no"}\n`;
|
|
141
|
+
message += `Build script: ${env.hasBuildScript ? "yes" : "no"}\n`;
|
|
142
|
+
|
|
143
|
+
if (env.devServerRunning) {
|
|
144
|
+
message += `Dev server: running\n`;
|
|
145
|
+
}
|
|
146
|
+
if (env.buildServerRunning) {
|
|
147
|
+
message += `Build server: running\n`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (env.ramTotalGb !== null || env.ramAvailableGb !== null) {
|
|
151
|
+
message += `RAM: ${formatRamForAgent(env)}\n`;
|
|
152
|
+
if (env.lowRamWarning) {
|
|
153
|
+
message += `WARNING: Low RAM - avoid parallel build + dev\n`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
message += `\n`;
|
|
158
|
+
message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
|
|
159
|
+
|
|
160
|
+
if (env.mode === "unknown") {
|
|
161
|
+
message += `\nNOTE: Environment unknown - a dev/build script only means a server CAN be started, it does not establish the workspace mode. Explicitly ask the user before build.\n`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return message;
|
|
165
|
+
}
|
|
@@ -14,6 +14,13 @@ import { DatabaseSync } from "node:sqlite";
|
|
|
14
14
|
import { existsSync, readFileSync, mkdirSync } from "fs";
|
|
15
15
|
import { join } from "path";
|
|
16
16
|
import { platform, totalmem, freemem } from "os";
|
|
17
|
+
import {
|
|
18
|
+
describeServerState,
|
|
19
|
+
describeWorkspaceMode,
|
|
20
|
+
formatEnvForAgentResult,
|
|
21
|
+
formatEnvStatusLine as formatStatusLine,
|
|
22
|
+
} from "./workspace-env-format.js";
|
|
23
|
+
import type { EnvEvidence, WorkspaceEnvResult, WorkspaceMode } from "./workspace-env-format.js";
|
|
17
24
|
|
|
18
25
|
const STORE_DIR = join(process.env.HOME || "/home/frappe", ".pi-harness-runtime");
|
|
19
26
|
|
|
@@ -63,39 +70,16 @@ function initDatabase(): DatabaseSync {
|
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
/**
|
|
66
|
-
*
|
|
73
|
+
* The mode/evidence types and every human readable rendering of the workspace
|
|
74
|
+
* environment live in workspace-env-format.ts. They are re-exported here so
|
|
75
|
+
* the existing import paths keep working.
|
|
67
76
|
*/
|
|
68
|
-
export
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
export
|
|
74
|
-
source: "process.env" | ".env file" | "package.json" | "command" | "detection";
|
|
75
|
-
key: string;
|
|
76
|
-
value: string;
|
|
77
|
-
timestamp: number;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Workspace environment result
|
|
82
|
-
*/
|
|
83
|
-
export interface WorkspaceEnvResult {
|
|
84
|
-
workspacePath: string;
|
|
85
|
-
mode: WorkspaceMode;
|
|
86
|
-
evidence: EnvEvidence[];
|
|
87
|
-
hasDevScript: boolean;
|
|
88
|
-
hasBuildScript: boolean;
|
|
89
|
-
packageManager: string | null;
|
|
90
|
-
devServerRunning: boolean;
|
|
91
|
-
buildServerRunning: boolean;
|
|
92
|
-
ramTotalGb: number | null;
|
|
93
|
-
ramAvailableGb: number | null;
|
|
94
|
-
ramEffectiveLimitGb: number | null;
|
|
95
|
-
lowRamWarning: boolean;
|
|
96
|
-
buildAllowed: boolean;
|
|
97
|
-
detectedAt: number;
|
|
98
|
-
}
|
|
77
|
+
export {
|
|
78
|
+
describeServerState,
|
|
79
|
+
describeWorkspaceMode,
|
|
80
|
+
formatEnvForAgentResult,
|
|
81
|
+
};
|
|
82
|
+
export type { EnvEvidence, WorkspaceEnvResult, WorkspaceMode };
|
|
99
83
|
|
|
100
84
|
/**
|
|
101
85
|
* Parse dotenv file without loading secrets
|
|
@@ -191,31 +175,22 @@ function determineMode(
|
|
|
191
175
|
// Explicit process.env takes highest priority (for build processes)
|
|
192
176
|
if (processEnv) {
|
|
193
177
|
const env = processEnv.toLowerCase();
|
|
178
|
+
// The evidence is recorded for EVERY value, including one that names
|
|
179
|
+
// no known mode ("staging"): the label then says which NODE_ENV could
|
|
180
|
+
// not be interpreted instead of claiming there is none.
|
|
181
|
+
evidence.push({
|
|
182
|
+
source: "process.env",
|
|
183
|
+
key: "NODE_ENV",
|
|
184
|
+
value: env,
|
|
185
|
+
timestamp: Date.now(),
|
|
186
|
+
});
|
|
194
187
|
if (env === "production") {
|
|
195
|
-
evidence.push({
|
|
196
|
-
source: "process.env",
|
|
197
|
-
key: "NODE_ENV",
|
|
198
|
-
value: env,
|
|
199
|
-
timestamp: Date.now(),
|
|
200
|
-
});
|
|
201
188
|
return "production";
|
|
202
189
|
}
|
|
203
190
|
if (env === "development" || env === "dev") {
|
|
204
|
-
evidence.push({
|
|
205
|
-
source: "process.env",
|
|
206
|
-
key: "NODE_ENV",
|
|
207
|
-
value: env,
|
|
208
|
-
timestamp: Date.now(),
|
|
209
|
-
});
|
|
210
191
|
return "development";
|
|
211
192
|
}
|
|
212
193
|
if (env === "test") {
|
|
213
|
-
evidence.push({
|
|
214
|
-
source: "process.env",
|
|
215
|
-
key: "NODE_ENV",
|
|
216
|
-
value: env,
|
|
217
|
-
timestamp: Date.now(),
|
|
218
|
-
});
|
|
219
194
|
return "test";
|
|
220
195
|
}
|
|
221
196
|
}
|
|
@@ -474,59 +449,12 @@ export function revalidateWorkspaceEnv(workspacePath: string): WorkspaceEnvResul
|
|
|
474
449
|
* Format workspace env as agent-readable message
|
|
475
450
|
*/
|
|
476
451
|
export function formatEnvForAgent(workspacePath: string): string {
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
let message = `[Workspace Environment]\n`;
|
|
480
|
-
message += `Mode: ${env.mode.toUpperCase()}\n`;
|
|
481
|
-
|
|
482
|
-
if (env.evidence.length > 0) {
|
|
483
|
-
const sources = env.evidence.map((e) => `${e.source}:${e.value}`).join(", ");
|
|
484
|
-
message += `Evidence: ${sources}\n`;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
message += `Dev script: ${env.hasDevScript ? "yes" : "no"}\n`;
|
|
488
|
-
message += `Build script: ${env.hasBuildScript ? "yes" : "no"}\n`;
|
|
489
|
-
|
|
490
|
-
if (env.devServerRunning) {
|
|
491
|
-
message += `Dev server: running\n`;
|
|
492
|
-
}
|
|
493
|
-
if (env.buildServerRunning) {
|
|
494
|
-
message += `Build server: running\n`;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
if (env.ramTotalGb !== null) {
|
|
498
|
-
const ramStr = env.ramAvailableGb !== null
|
|
499
|
-
? `${env.ramAvailableGb}/${env.ramTotalGb}GB available`
|
|
500
|
-
: `${env.ramTotalGb}GB total`;
|
|
501
|
-
message += `RAM: ${ramStr}\n`;
|
|
502
|
-
if (env.lowRamWarning) {
|
|
503
|
-
message += `WARNING: Low RAM - avoid parallel build + dev\n`;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
message += `\n`;
|
|
508
|
-
message += `Build allowed: ${env.buildAllowed ? "yes" : "no - requires explicit request"}\n`;
|
|
509
|
-
|
|
510
|
-
if (env.mode === "unknown") {
|
|
511
|
-
message += `\nNOTE: Environment unknown. Explicitly ask user before build.\n`;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return message;
|
|
452
|
+
return formatEnvForAgentResult(getWorkspaceEnv(workspacePath));
|
|
515
453
|
}
|
|
516
454
|
|
|
517
455
|
/**
|
|
518
456
|
* Format workspace env as compact status line for UI
|
|
519
457
|
*/
|
|
520
458
|
export function formatEnvStatusLine(env: WorkspaceEnvResult): string {
|
|
521
|
-
|
|
522
|
-
const devStatus = env.devServerRunning ? "running" : (env.hasDevScript ? "available" : "none");
|
|
523
|
-
const buildStatus = env.buildServerRunning ? "running" : (env.hasBuildScript ? "explicit request" : "none");
|
|
524
|
-
const ramStr = env.ramAvailableGb !== null ? `${env.ramAvailableGb}/${env.ramTotalGb}` : "";
|
|
525
|
-
|
|
526
|
-
let status = `Workspace: ${mode} | dev server: ${devStatus} | build: ${buildStatus}`;
|
|
527
|
-
if (ramStr) {
|
|
528
|
-
status += ` | RAM: ${ramStr}`;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return status;
|
|
459
|
+
return formatStatusLine(env);
|
|
532
460
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
describeServerState,
|
|
6
|
+
describeWorkspaceMode,
|
|
7
|
+
formatEnvForAgentResult,
|
|
8
|
+
formatEnvStatusLine,
|
|
9
|
+
} from "../src/workspace-env-format.ts";
|
|
10
|
+
import type { EnvEvidence, WorkspaceEnvResult } from "../src/workspace-env-format.ts";
|
|
11
|
+
|
|
12
|
+
function makeEnv(overrides: Partial<WorkspaceEnvResult> = {}): WorkspaceEnvResult {
|
|
13
|
+
return {
|
|
14
|
+
workspacePath: "/home/frappe/pi-harness-runtime",
|
|
15
|
+
mode: "unknown",
|
|
16
|
+
evidence: [],
|
|
17
|
+
hasDevScript: false,
|
|
18
|
+
hasBuildScript: false,
|
|
19
|
+
packageManager: "bun",
|
|
20
|
+
devServerRunning: false,
|
|
21
|
+
buildServerRunning: false,
|
|
22
|
+
ramTotalGb: 15,
|
|
23
|
+
ramAvailableGb: 7.7,
|
|
24
|
+
ramEffectiveLimitGb: null,
|
|
25
|
+
lowRamWarning: true,
|
|
26
|
+
buildAllowed: false,
|
|
27
|
+
detectedAt: 0,
|
|
28
|
+
...overrides,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("workspace mode wording", () => {
|
|
33
|
+
test("an unknown mode carries its reason", () => {
|
|
34
|
+
expect(describeWorkspaceMode(makeEnv())).toBe("UNKNOWN (no explicit NODE_ENV)");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("an unrecognized NODE_ENV names the value that could not be read", () => {
|
|
38
|
+
const evidence: EnvEvidence[] = [
|
|
39
|
+
{ source: "process.env", key: "NODE_ENV", value: "staging", timestamp: 0 },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
expect(describeWorkspaceMode(makeEnv({ evidence }))).toBe(
|
|
43
|
+
"UNKNOWN (NODE_ENV=staging is not a known mode)",
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("a known mode is reported plainly", () => {
|
|
48
|
+
expect(describeWorkspaceMode(makeEnv({ mode: "development" }))).toBe("DEVELOPMENT");
|
|
49
|
+
expect(describeWorkspaceMode(makeEnv({ mode: "production" }))).toBe("PRODUCTION");
|
|
50
|
+
expect(describeWorkspaceMode(makeEnv({ mode: "test" }))).toBe("TEST");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("a dev script is a capability, not a state", () => {
|
|
54
|
+
expect(describeServerState(false, true)).toBe("not running (script available)");
|
|
55
|
+
expect(describeServerState(true, true)).toBe("running");
|
|
56
|
+
expect(describeServerState(false, false)).toBe("no script");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("formatEnvStatusLine", () => {
|
|
61
|
+
test("does not show a bare UNKNOWN next to an available dev script", () => {
|
|
62
|
+
const line = formatEnvStatusLine(makeEnv({ hasDevScript: true, hasBuildScript: true }));
|
|
63
|
+
|
|
64
|
+
expect(line).toContain("Workspace: UNKNOWN (no explicit NODE_ENV)");
|
|
65
|
+
expect(line).not.toContain("Workspace: UNKNOWN |");
|
|
66
|
+
expect(line).toContain("dev server: not running (script available)");
|
|
67
|
+
expect(line).toContain("build: not running (script available)");
|
|
68
|
+
expect(line).toContain("RAM: 7.7/15");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("a running dev server is reported as running", () => {
|
|
72
|
+
const line = formatEnvStatusLine(makeEnv({ mode: "development", hasDevScript: true, devServerRunning: true }));
|
|
73
|
+
|
|
74
|
+
expect(line).toContain("Workspace: DEVELOPMENT");
|
|
75
|
+
expect(line).toContain("dev server: running");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("a missing RAM reading omits the RAM segment", () => {
|
|
79
|
+
const line = formatEnvStatusLine(makeEnv({ ramTotalGb: null, ramAvailableGb: null }));
|
|
80
|
+
|
|
81
|
+
expect(line).not.toContain("RAM:");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("a partial RAM reading never renders a null value", () => {
|
|
85
|
+
const available = formatEnvStatusLine(makeEnv({ ramTotalGb: null, ramAvailableGb: 7.7 }));
|
|
86
|
+
const total = formatEnvStatusLine(makeEnv({ ramTotalGb: 15, ramAvailableGb: null }));
|
|
87
|
+
|
|
88
|
+
expect(available).toContain("RAM: 7.7GB available");
|
|
89
|
+
expect(available).not.toContain("null");
|
|
90
|
+
expect(total).toContain("RAM: 15GB total");
|
|
91
|
+
expect(total).not.toContain("null");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("export surface of workspace-env.ts", () => {
|
|
96
|
+
// The module itself needs node:sqlite, which bun 1.3.x does not provide.
|
|
97
|
+
// Assert the re-export surface on the source text instead.
|
|
98
|
+
const source = readFileSync(
|
|
99
|
+
join(import.meta.dir, "..", "src", "workspace-env.ts"),
|
|
100
|
+
"utf8",
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
test("keeps re-exporting the formatting helpers", () => {
|
|
104
|
+
for (const name of [
|
|
105
|
+
"describeServerState",
|
|
106
|
+
"describeWorkspaceMode",
|
|
107
|
+
"formatEnvForAgentResult",
|
|
108
|
+
]) {
|
|
109
|
+
expect(source).toContain(name);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("keeps exporting the detector functions", () => {
|
|
114
|
+
for (const name of [
|
|
115
|
+
"detectWorkspaceEnv",
|
|
116
|
+
"loadWorkspaceEnv",
|
|
117
|
+
"getWorkspaceEnv",
|
|
118
|
+
"revalidateWorkspaceEnv",
|
|
119
|
+
"formatEnvForAgent",
|
|
120
|
+
"formatEnvStatusLine",
|
|
121
|
+
]) {
|
|
122
|
+
expect(source).toContain(`export function ${name}`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("formatEnvForAgentResult", () => {
|
|
128
|
+
test("explains that a dev script does not establish the mode", () => {
|
|
129
|
+
const text = formatEnvForAgentResult(makeEnv({ hasDevScript: true }));
|
|
130
|
+
|
|
131
|
+
expect(text).toContain("Mode: UNKNOWN (no explicit NODE_ENV)");
|
|
132
|
+
expect(text).toContain("Dev script: yes");
|
|
133
|
+
expect(text).toContain("Build allowed: no - requires explicit request");
|
|
134
|
+
expect(text).toContain("a dev/build script only means a server CAN be started");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("a known mode drops the unknown-mode note", () => {
|
|
138
|
+
const text = formatEnvForAgentResult(makeEnv({ mode: "production", buildAllowed: true }));
|
|
139
|
+
|
|
140
|
+
expect(text).toContain("Mode: PRODUCTION");
|
|
141
|
+
expect(text).toContain("Build allowed: yes");
|
|
142
|
+
expect(text).not.toContain("Environment unknown");
|
|
143
|
+
});
|
|
144
|
+
});
|