archbyte 0.6.0 → 0.7.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.
@@ -0,0 +1,86 @@
1
+ // Session Reader — reads session data from .archbyte/sessions/
2
+ import { readFile, readdir } from "fs/promises";
3
+ import { existsSync, readFileSync } from "fs";
4
+ import path from "path";
5
+ function sessionsDir(projectRoot) {
6
+ return path.join(projectRoot, ".archbyte", "sessions");
7
+ }
8
+ /** Load session index — reads index.json, or scans session dirs if missing */
9
+ export async function loadSessionIndex(projectRoot) {
10
+ const dir = sessionsDir(projectRoot);
11
+ const indexPath = path.join(dir, "index.json");
12
+ // Try cached index first
13
+ if (existsSync(indexPath)) {
14
+ try {
15
+ const raw = readFileSync(indexPath, "utf-8");
16
+ return JSON.parse(raw);
17
+ }
18
+ catch {
19
+ // Corrupted — rebuild
20
+ }
21
+ }
22
+ // Scan session directories
23
+ return rebuildIndex(projectRoot);
24
+ }
25
+ /** Rebuild index by scanning all session dirs */
26
+ async function rebuildIndex(projectRoot) {
27
+ const dir = sessionsDir(projectRoot);
28
+ if (!existsSync(dir))
29
+ return { sessions: [] };
30
+ const entries = await readdir(dir, { withFileTypes: true });
31
+ const sessions = [];
32
+ for (const entry of entries) {
33
+ if (!entry.isDirectory())
34
+ continue;
35
+ const sessionPath = path.join(dir, entry.name, "session.json");
36
+ if (!existsSync(sessionPath))
37
+ continue;
38
+ try {
39
+ const raw = await readFile(sessionPath, "utf-8");
40
+ const session = JSON.parse(raw);
41
+ sessions.push({
42
+ sessionId: session.sessionId,
43
+ startedAt: session.startedAt,
44
+ completedAt: session.completedAt,
45
+ status: session.status,
46
+ runCount: session.summary.totalRuns,
47
+ phases: session.summary.phases,
48
+ source: session.source,
49
+ });
50
+ }
51
+ catch {
52
+ // Skip corrupted session files
53
+ }
54
+ }
55
+ // Sort newest first
56
+ sessions.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
57
+ return { sessions };
58
+ }
59
+ /** Load a full session by ID */
60
+ export async function loadSession(projectRoot, sessionId) {
61
+ const sessionPath = path.join(sessionsDir(projectRoot), sessionId, "session.json");
62
+ if (!existsSync(sessionPath))
63
+ return null;
64
+ try {
65
+ const raw = await readFile(sessionPath, "utf-8");
66
+ return JSON.parse(raw);
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ /** Read recent activity log entries (newest first) */
73
+ export async function loadRecentActivity(projectRoot, limit = 50) {
74
+ const logPath = path.join(sessionsDir(projectRoot), "activity.log");
75
+ if (!existsSync(logPath))
76
+ return [];
77
+ try {
78
+ const raw = await readFile(logPath, "utf-8");
79
+ const lines = raw.trim().split("\n").filter(Boolean);
80
+ // Return newest first, limited
81
+ return lines.slice(-limit).reverse();
82
+ }
83
+ catch {
84
+ return [];
85
+ }
86
+ }
@@ -0,0 +1,125 @@
1
+ export interface TranscriptEvent {
2
+ timestamp: string;
3
+ type: 'user' | 'assistant' | 'thinking' | 'tool_use' | 'tool_result' | 'system';
4
+ agentId?: string;
5
+ agentSlug?: string;
6
+ text?: string;
7
+ toolName?: string;
8
+ toolUseId?: string;
9
+ toolInput?: string;
10
+ toolResult?: string;
11
+ toolSuccess?: boolean;
12
+ model?: string;
13
+ usage?: {
14
+ inputTokens: number;
15
+ outputTokens: number;
16
+ cacheReadTokens: number;
17
+ cacheCreationTokens: number;
18
+ };
19
+ }
20
+ export interface CloudStats {
21
+ totalInputTokens: number;
22
+ totalOutputTokens: number;
23
+ totalCacheReadTokens: number;
24
+ totalCacheCreationTokens: number;
25
+ estimatedCost: {
26
+ input: number;
27
+ output: number;
28
+ cacheRead: number;
29
+ cacheCreation: number;
30
+ total: number;
31
+ };
32
+ models: string[];
33
+ apiCalls: number;
34
+ }
35
+ export interface PayloadFile {
36
+ path: string;
37
+ timestamp: string;
38
+ agentId?: string;
39
+ }
40
+ export interface AgentRun {
41
+ runId: string;
42
+ sessionId: string;
43
+ phase: string;
44
+ passId: string;
45
+ agent: string;
46
+ model: string;
47
+ iteration: number;
48
+ startedAt: string;
49
+ completedAt?: string;
50
+ elapsedSeconds?: number;
51
+ status: "running" | "success" | "error" | "skipped";
52
+ error?: string;
53
+ tokens?: {
54
+ input: number;
55
+ output: number;
56
+ };
57
+ cost?: {
58
+ input: number;
59
+ output: number;
60
+ total: number;
61
+ };
62
+ toolCalls?: Array<{
63
+ name: string;
64
+ durationMs: number;
65
+ success: boolean;
66
+ }>;
67
+ artifacts?: {
68
+ prompt?: string;
69
+ output?: string;
70
+ error?: string;
71
+ };
72
+ agentType?: string;
73
+ transcript?: TranscriptEvent[];
74
+ }
75
+ export interface AgentSession {
76
+ sessionId: string;
77
+ projectName?: string;
78
+ startedAt: string;
79
+ completedAt?: string;
80
+ status: "running" | "success" | "error" | "partial";
81
+ source: string;
82
+ projectMeta?: {
83
+ topic?: string;
84
+ mode?: string;
85
+ model?: string;
86
+ style?: string;
87
+ };
88
+ summary: {
89
+ totalRuns: number;
90
+ successfulRuns: number;
91
+ failedRuns: number;
92
+ skippedRuns: number;
93
+ totalElapsedSeconds: number;
94
+ totalTokens: {
95
+ input: number;
96
+ output: number;
97
+ };
98
+ phases: string[];
99
+ models: string[];
100
+ };
101
+ runs: AgentRun[];
102
+ transcript?: TranscriptEvent[];
103
+ cloudStats?: CloudStats;
104
+ payload?: PayloadFile[];
105
+ }
106
+ export interface SessionIndex {
107
+ sessions: Array<{
108
+ sessionId: string;
109
+ startedAt: string;
110
+ completedAt?: string;
111
+ status: AgentSession["status"];
112
+ runCount: number;
113
+ phases: string[];
114
+ source: string;
115
+ category?: string;
116
+ label?: string;
117
+ touchedDirs?: string[];
118
+ eventCount?: number;
119
+ dirMetrics?: Record<string, {
120
+ reads: number;
121
+ writes: number;
122
+ }>;
123
+ estimatedCost?: number;
124
+ }>;
125
+ }
@@ -0,0 +1,3 @@
1
+ // Agent Session Observability — generic schema
2
+ // Any agent framework (archbyte pipeline, custom) emits these types.
3
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { AgentRun, AgentSession } from "./types.js";
2
+ export declare function createSession(projectRoot: string, sessionId: string, opts?: {
3
+ projectName?: string;
4
+ source?: string;
5
+ }): Promise<AgentSession>;
6
+ export declare function writeRun(projectRoot: string, sessionId: string, run: AgentRun): Promise<void>;
7
+ export declare function completeSession(projectRoot: string, sessionId: string, runs: AgentRun[]): Promise<void>;
8
+ export declare function updateSessionIndex(projectRoot: string): Promise<void>;
9
+ export declare function appendActivityLog(projectRoot: string, entry: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,100 @@
1
+ // Session Writer — writes session data to .archbyte/sessions/
2
+ import { mkdir, writeFile, appendFile } from "fs/promises";
3
+ import path from "path";
4
+ import { loadSessionIndex } from "./reader.js";
5
+ function sessionsDir(projectRoot) {
6
+ return path.join(projectRoot, ".archbyte", "sessions");
7
+ }
8
+ export async function createSession(projectRoot, sessionId, opts) {
9
+ const dir = path.join(sessionsDir(projectRoot), sessionId);
10
+ await mkdir(path.join(dir, "runs"), { recursive: true });
11
+ const session = {
12
+ sessionId,
13
+ projectName: opts?.projectName,
14
+ startedAt: new Date().toISOString(),
15
+ status: "running",
16
+ source: opts?.source ?? "archbyte",
17
+ summary: {
18
+ totalRuns: 0,
19
+ successfulRuns: 0,
20
+ failedRuns: 0,
21
+ skippedRuns: 0,
22
+ totalElapsedSeconds: 0,
23
+ totalTokens: { input: 0, output: 0 },
24
+ phases: [],
25
+ models: [],
26
+ },
27
+ runs: [],
28
+ };
29
+ await writeFile(path.join(dir, "session.json"), JSON.stringify(session, null, 2), "utf-8");
30
+ return session;
31
+ }
32
+ export async function writeRun(projectRoot, sessionId, run) {
33
+ const runDir = path.join(sessionsDir(projectRoot), sessionId, "runs", run.runId);
34
+ await mkdir(runDir, { recursive: true });
35
+ await writeFile(path.join(runDir, "meta.json"), JSON.stringify(run, null, 2), "utf-8");
36
+ // Write optional artifacts
37
+ if (run.artifacts?.prompt) {
38
+ await writeFile(path.join(runDir, "prompt.md"), run.artifacts.prompt, "utf-8");
39
+ }
40
+ if (run.artifacts?.output) {
41
+ await writeFile(path.join(runDir, "output.md"), run.artifacts.output, "utf-8");
42
+ }
43
+ if (run.artifacts?.error) {
44
+ await writeFile(path.join(runDir, "error.txt"), run.artifacts.error, "utf-8");
45
+ }
46
+ // Append to activity log
47
+ await appendActivityLog(projectRoot, {
48
+ type: "run",
49
+ sessionId,
50
+ runId: run.runId,
51
+ agent: run.agent,
52
+ status: run.status,
53
+ timestamp: run.completedAt ?? run.startedAt,
54
+ });
55
+ }
56
+ export async function completeSession(projectRoot, sessionId, runs) {
57
+ const dir = path.join(sessionsDir(projectRoot), sessionId);
58
+ const successfulRuns = runs.filter((r) => r.status === "success").length;
59
+ const failedRuns = runs.filter((r) => r.status === "error").length;
60
+ const skippedRuns = runs.filter((r) => r.status === "skipped").length;
61
+ const totalElapsed = runs.reduce((s, r) => s + (r.elapsedSeconds ?? 0), 0);
62
+ const totalTokensIn = runs.reduce((s, r) => s + (r.tokens?.input ?? 0), 0);
63
+ const totalTokensOut = runs.reduce((s, r) => s + (r.tokens?.output ?? 0), 0);
64
+ const phases = [...new Set(runs.map((r) => r.phase))];
65
+ const models = [...new Set(runs.map((r) => r.model))];
66
+ const hasErrors = failedRuns > 0;
67
+ const allFailed = failedRuns === runs.length;
68
+ const session = {
69
+ sessionId,
70
+ startedAt: runs[0]?.startedAt ?? new Date().toISOString(),
71
+ completedAt: new Date().toISOString(),
72
+ status: allFailed ? "error" : hasErrors ? "partial" : "success",
73
+ source: "archbyte",
74
+ summary: {
75
+ totalRuns: runs.length,
76
+ successfulRuns,
77
+ failedRuns,
78
+ skippedRuns,
79
+ totalElapsedSeconds: totalElapsed,
80
+ totalTokens: { input: totalTokensIn, output: totalTokensOut },
81
+ phases,
82
+ models,
83
+ },
84
+ runs,
85
+ };
86
+ await writeFile(path.join(dir, "session.json"), JSON.stringify(session, null, 2), "utf-8");
87
+ // Rebuild index
88
+ await updateSessionIndex(projectRoot);
89
+ }
90
+ export async function updateSessionIndex(projectRoot) {
91
+ const index = await loadSessionIndex(projectRoot);
92
+ await mkdir(sessionsDir(projectRoot), { recursive: true });
93
+ await writeFile(path.join(sessionsDir(projectRoot), "index.json"), JSON.stringify(index, null, 2), "utf-8");
94
+ }
95
+ export async function appendActivityLog(projectRoot, entry) {
96
+ const logPath = path.join(sessionsDir(projectRoot), "activity.log");
97
+ await mkdir(sessionsDir(projectRoot), { recursive: true });
98
+ const line = JSON.stringify({ ...entry, _ts: new Date().toISOString() }) + "\n";
99
+ await appendFile(logPath, line, "utf-8");
100
+ }
@@ -6,7 +6,7 @@ import type { IncrementalContext } from "./types.js";
6
6
  * Run the multi-agent pipeline: 3 parallel fast agents → 2 sequential agents.
7
7
  * Each agent gets a single chat() call with pre-collected static context.
8
8
  */
9
- export declare function runPipeline(ctx: StaticContext, provider: LLMProvider, config: ArchByteConfig, onProgress?: (msg: string) => void, incrementalContext?: IncrementalContext, onDebug?: (agentId: string, model: string, system: string, user: string) => void): Promise<StaticAnalysisResult & {
9
+ export declare function runPipeline(ctx: StaticContext, provider: LLMProvider, config: ArchByteConfig, onProgress?: (msg: string) => void, incrementalContext?: IncrementalContext, onDebug?: (agentId: string, model: string, system: string, user: string) => void, projectRoot?: string): Promise<StaticAnalysisResult & {
10
10
  tokenUsage?: {
11
11
  input: number;
12
12
  output: number;
@@ -1,5 +1,7 @@
1
1
  // Pipeline — Orchestrator
2
2
  // Runs static context collection → parallel LLM agents → sequential LLM agents
3
+ import { writeFile, mkdir } from "fs/promises";
4
+ import path from "path";
3
5
  import { resolveModel } from "../runtime/types.js";
4
6
  import { validateAnalysis } from "../static/validator.js";
5
7
  import { componentIdentifier } from "./agents/component-identifier.js";
@@ -92,14 +94,40 @@ function getFallbackData(agentId, inc) {
92
94
  * Run the multi-agent pipeline: 3 parallel fast agents → 2 sequential agents.
93
95
  * Each agent gets a single chat() call with pre-collected static context.
94
96
  */
95
- export async function runPipeline(ctx, provider, config, onProgress, incrementalContext, onDebug) {
97
+ export async function runPipeline(ctx, provider, config, onProgress, incrementalContext, onDebug, projectRoot) {
96
98
  const agentResults = {};
97
99
  const agentMeta = [];
98
100
  const skippedAgents = [];
101
+ const sessionId = `archbyte-${Date.now()}`;
102
+ const sessionRuns = [];
99
103
  // Pass incremental context to agents via priorResults
100
104
  if (incrementalContext) {
101
105
  agentResults["__incremental__"] = incrementalContext;
102
106
  }
107
+ // Helper: record an agent run for session observability
108
+ function recordRun(agent, result, status, error) {
109
+ const model = resolveModel(config.provider, agent.modelTier, config.modelOverrides, config.model);
110
+ const run = {
111
+ runId: `${agent.id}-${Date.now()}`,
112
+ sessionId,
113
+ phase: agent.id === "connection-mapper" || agent.id === "validator" ? "sequential" : "parallel",
114
+ passId: agent.id,
115
+ agent: agent.name,
116
+ model,
117
+ iteration: 1,
118
+ startedAt: result ? new Date(Date.now() - result.duration).toISOString() : new Date().toISOString(),
119
+ completedAt: new Date().toISOString(),
120
+ elapsedSeconds: result ? result.duration / 1000 : 0,
121
+ status,
122
+ error,
123
+ tokens: result?.tokenUsage ? { input: result.tokenUsage.input, output: result.tokenUsage.output } : undefined,
124
+ artifacts: result?.prompt || result?.rawOutput ? {
125
+ ...(result.prompt ? { prompt: result.prompt } : {}),
126
+ ...(result.rawOutput ? { output: result.rawOutput } : {}),
127
+ } : undefined,
128
+ };
129
+ sessionRuns.push(run);
130
+ }
103
131
  // === Phase 1: Parallel agents ===
104
132
  onProgress?.(`Phase 1: Running ${PARALLEL_AGENTS.length} agents in parallel...`);
105
133
  // Pass agentResults to parallel agents too (contains __incremental__ if set)
@@ -123,17 +151,21 @@ export async function runPipeline(ctx, provider, config, onProgress, incremental
123
151
  let authFailed = false;
124
152
  for (let i = 0; i < parallelTasks.length; i++) {
125
153
  const { agent, skipReason } = parallelTasks[i];
126
- if (skipReason)
127
- continue; // Already handled
154
+ if (skipReason) {
155
+ recordRun(agent, null, "skipped");
156
+ continue;
157
+ }
128
158
  const result = parallelResults[i];
129
159
  if (result.status === "fulfilled" && result.value) {
130
160
  agentResults[agent.id] = result.value.data;
131
161
  agentMeta.push(result.value);
132
162
  onProgress?.(` ${agent.name}: done (${(result.value.duration / 1000).toFixed(1)}s)`);
163
+ recordRun(agent, result.value, "success");
133
164
  }
134
165
  else {
135
166
  const reason = result.status === "rejected" ? result.reason : "null response";
136
167
  onProgress?.(` ${agent.name}: failed (${reason})`);
168
+ recordRun(agent, null, "error", String(reason));
137
169
  if (result.status === "rejected" && isAuthError(result.reason)) {
138
170
  authFailed = true;
139
171
  }
@@ -153,6 +185,7 @@ export async function runPipeline(ctx, provider, config, onProgress, incremental
153
185
  onProgress?.(` ${agent.name}: skipped (${skipReason})`);
154
186
  const fallback = getFallbackData(agent.id, incrementalContext);
155
187
  agentResults[agent.id] = fallback;
188
+ recordRun(agent, null, "skipped");
156
189
  continue;
157
190
  }
158
191
  try {
@@ -161,13 +194,16 @@ export async function runPipeline(ctx, provider, config, onProgress, incremental
161
194
  agentResults[agent.id] = result.data;
162
195
  agentMeta.push(result);
163
196
  onProgress?.(` ${agent.name}: done (${(result.duration / 1000).toFixed(1)}s)`);
197
+ recordRun(agent, result, "success");
164
198
  }
165
199
  else {
166
200
  onProgress?.(` ${agent.name}: returned null`);
201
+ recordRun(agent, null, "error", "returned null");
167
202
  }
168
203
  }
169
204
  catch (err) {
170
205
  onProgress?.(` ${agent.name}: failed (${err instanceof Error ? err.message : String(err)})`);
206
+ recordRun(agent, null, "error", err instanceof Error ? err.message : String(err));
171
207
  if (isAuthError(err)) {
172
208
  throw new Error(`API key authentication failed. Check your API key with: archbyte config set api-key <your-key>\n` +
173
209
  ` Current provider: ${config.provider}. Get a valid key from your provider's dashboard.`);
@@ -204,6 +240,50 @@ export async function runPipeline(ctx, provider, config, onProgress, incremental
204
240
  console.error();
205
241
  onProgress?.(`Token usage: ${totalInput} in / ${totalOutput} out${skippedAgents.length > 0 ? ` (${skippedAgents.length} skipped)` : ""}`);
206
242
  }
243
+ // === Write runs to .archbyte/runs/ (best-effort, fire-and-forget) ===
244
+ if (projectRoot) {
245
+ const sessionStartedAt = sessionRuns.length > 0
246
+ ? sessionRuns.reduce((earliest, r) => r.startedAt < earliest ? r.startedAt : earliest, sessionRuns[0].startedAt)
247
+ : new Date().toISOString();
248
+ const sessionCompletedAt = new Date().toISOString();
249
+ const writeRuns = async () => {
250
+ const runsDir = path.join(projectRoot, ".archbyte", "runs");
251
+ for (const run of sessionRuns) {
252
+ const agentDir = path.join(runsDir, run.passId);
253
+ await mkdir(agentDir, { recursive: true });
254
+ // meta.json — AgentRun fields minus artifacts
255
+ const { artifacts, ...meta } = run;
256
+ await writeFile(path.join(agentDir, "meta.json"), JSON.stringify(meta, null, 2));
257
+ // prompt.md and output.md from artifacts
258
+ if (artifacts?.prompt) {
259
+ await writeFile(path.join(agentDir, "prompt.md"), artifacts.prompt);
260
+ }
261
+ if (artifacts?.output) {
262
+ await writeFile(path.join(agentDir, "output.md"), artifacts.output);
263
+ }
264
+ }
265
+ // session.json — lightweight summary
266
+ const successCount = sessionRuns.filter(r => r.status === "success").length;
267
+ const failedCount = sessionRuns.filter(r => r.status === "error").length;
268
+ const session = {
269
+ sessionId,
270
+ startedAt: sessionStartedAt,
271
+ completedAt: sessionCompletedAt,
272
+ status: failedCount > 0 ? "partial" : "success",
273
+ source: "archbyte",
274
+ mode: "pipeline",
275
+ totalRuns: sessionRuns.length,
276
+ successfulRuns: successCount,
277
+ failedRuns: failedCount,
278
+ totalTokens: { input: totalInput, output: totalOutput },
279
+ totalElapsedSeconds: sessionRuns.reduce((s, r) => s + (r.elapsedSeconds ?? 0), 0),
280
+ };
281
+ await writeFile(path.join(projectRoot, ".archbyte", "session.json"), JSON.stringify(session, null, 2));
282
+ };
283
+ writeRuns().catch((err) => {
284
+ console.error("[pipeline] Failed to write runs:", err);
285
+ });
286
+ }
207
287
  return { ...merged, tokenUsage: { input: totalInput, output: totalOutput }, skippedAgents: skippedAgents.length > 0 ? skippedAgents : undefined };
208
288
  }
209
289
  // Agents that produce large structured output (lists of components/connections) need more tokens
@@ -248,5 +328,7 @@ async function runAgent(agent, ctx, provider, config, priorResults, onProgress,
248
328
  data,
249
329
  duration: Date.now() - start,
250
330
  tokenUsage: { input: response.usage.inputTokens, output: response.usage.outputTokens },
331
+ prompt: system + "\n\n---\n\n" + user,
332
+ rawOutput: text,
251
333
  };
252
334
  }
@@ -171,4 +171,6 @@ export interface PipelineAgentResult {
171
171
  input: number;
172
172
  output: number;
173
173
  };
174
+ prompt?: string;
175
+ rawOutput?: string;
174
176
  }
@@ -96,6 +96,14 @@ function showConfig() {
96
96
  console.log(` ${chalk.bold("model")}: ${config.model}`);
97
97
  }
98
98
  }
99
+ const sessionsPaths = config.sessionsPaths;
100
+ if (Array.isArray(sessionsPaths) && sessionsPaths.length > 0) {
101
+ console.log();
102
+ console.log(` ${chalk.bold("sessions-path")}:`);
103
+ for (const p of sessionsPaths) {
104
+ console.log(` ${p}`);
105
+ }
106
+ }
99
107
  console.log();
100
108
  }
101
109
  function setConfig(key, value) {
@@ -151,9 +159,15 @@ function setConfig(key, value) {
151
159
  profiles[activeProvider2].model = value;
152
160
  break;
153
161
  }
162
+ case "sessions-path":
163
+ case "sessionsPath": {
164
+ // External directory paths for session observability (comma-separated)
165
+ config.sessionsPaths = value.split(",").map((p) => p.trim()).filter(Boolean);
166
+ break;
167
+ }
154
168
  default:
155
169
  console.error(chalk.red(`Unknown config key: ${key}`));
156
- console.error(chalk.gray(" Valid keys: provider, api-key, model"));
170
+ console.error(chalk.gray(" Valid keys: provider, api-key, model, sessions-path"));
157
171
  process.exit(1);
158
172
  }
159
173
  saveConfig(config);
@@ -182,6 +196,12 @@ function getConfig(key, raw = false) {
182
196
  case "model":
183
197
  console.log(profile?.model ?? config.model ?? "");
184
198
  break;
199
+ case "sessions-path":
200
+ case "sessionsPath": {
201
+ const paths = config.sessionsPaths;
202
+ console.log(Array.isArray(paths) ? paths.join(", ") : "");
203
+ break;
204
+ }
185
205
  default:
186
206
  console.error(chalk.red(`Unknown config key: ${key}`));
187
207
  process.exit(1);