@revealui/harnesses 0.1.0 → 0.1.1

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.
@@ -2,7 +2,12 @@ import {
2
2
  WorkboardManager,
3
3
  deriveSessionId,
4
4
  detectSessionType
5
- } from "./chunk-PG4RAOWS.js";
5
+ } from "./chunk-6WO7SSCX.js";
6
+
7
+ // src/index.ts
8
+ import { isFeatureEnabled } from "@revealui/core/features";
9
+ import { initializeLicense } from "@revealui/core/license";
10
+ import { logger } from "@revealui/core/observability/logger";
6
11
 
7
12
  // src/adapters/claude-code-adapter.ts
8
13
  import { execFile } from "child_process";
@@ -49,7 +54,22 @@ var ClaudeCodeAdapter = class {
49
54
  return false;
50
55
  }
51
56
  }
57
+ notifyRegistered() {
58
+ this.emit({ type: "harness-connected", harnessId: this.id });
59
+ }
60
+ notifyUnregistering() {
61
+ this.emit({ type: "harness-disconnected", harnessId: this.id });
62
+ }
52
63
  async execute(command) {
64
+ try {
65
+ return await this.executeInner(command);
66
+ } catch (err) {
67
+ const message = err instanceof Error ? err.message : String(err);
68
+ this.emit({ type: "error", harnessId: this.id, message });
69
+ return { success: false, command: command.type, message };
70
+ }
71
+ }
72
+ async executeInner(command) {
53
73
  switch (command.type) {
54
74
  case "get-status": {
55
75
  const available = await this.isAvailable();
@@ -117,6 +137,14 @@ var ClaudeCodeAdapter = class {
117
137
  async dispose() {
118
138
  this.eventHandlers.clear();
119
139
  }
140
+ emit(event) {
141
+ for (const handler of this.eventHandlers) {
142
+ try {
143
+ handler(event);
144
+ } catch {
145
+ }
146
+ }
147
+ }
120
148
  };
121
149
 
122
150
  // src/adapters/copilot-adapter.ts
@@ -141,6 +169,12 @@ var CopilotAdapter = class {
141
169
  async isAvailable() {
142
170
  return false;
143
171
  }
172
+ notifyRegistered() {
173
+ this.emit({ type: "harness-connected", harnessId: this.id });
174
+ }
175
+ notifyUnregistering() {
176
+ this.emit({ type: "harness-disconnected", harnessId: this.id });
177
+ }
144
178
  async execute(command) {
145
179
  return {
146
180
  success: false,
@@ -155,83 +189,147 @@ var CopilotAdapter = class {
155
189
  async dispose() {
156
190
  this.eventHandlers.clear();
157
191
  }
158
- };
159
-
160
- // src/detection/auto-detector.ts
161
- async function autoDetectHarnesses(registry) {
162
- const candidates = [new ClaudeCodeAdapter(), new CopilotAdapter()];
163
- const registered = [];
164
- await Promise.all(
165
- candidates.map(async (adapter) => {
192
+ emit(event) {
193
+ for (const handler of this.eventHandlers) {
166
194
  try {
167
- if (await adapter.isAvailable()) {
168
- registry.register(adapter);
169
- registered.push(adapter.id);
170
- } else {
171
- await adapter.dispose();
172
- }
195
+ handler(event);
173
196
  } catch {
174
- await adapter.dispose();
175
197
  }
176
- })
177
- );
178
- return registered;
179
- }
198
+ }
199
+ }
200
+ };
180
201
 
181
- // src/registry/harness-registry.ts
182
- var HarnessRegistry = class {
183
- adapters = /* @__PURE__ */ new Map();
184
- /** Register an adapter. Throws if an adapter with the same id already exists. */
185
- register(adapter) {
186
- if (this.adapters.has(adapter.id)) {
187
- throw new Error(`Harness adapter already registered: ${adapter.id}`);
202
+ // src/adapters/cursor-adapter.ts
203
+ import { execFile as execFile2 } from "child_process";
204
+ import { promisify as promisify2 } from "util";
205
+ var execFileAsync2 = promisify2(execFile2);
206
+ var CursorAdapter = class {
207
+ id = "cursor";
208
+ name = "Cursor";
209
+ eventHandlers = /* @__PURE__ */ new Set();
210
+ workboardPath;
211
+ constructor(workboardPath) {
212
+ this.workboardPath = workboardPath ?? process.env.REVEALUI_WORKBOARD_PATH;
213
+ }
214
+ getCapabilities() {
215
+ return {
216
+ generateCode: false,
217
+ analyzeCode: false,
218
+ applyEdit: false,
219
+ applyConfig: false,
220
+ readWorkboard: this.workboardPath !== void 0,
221
+ writeWorkboard: this.workboardPath !== void 0
222
+ };
223
+ }
224
+ async getInfo() {
225
+ let version;
226
+ try {
227
+ const { stdout } = await execFileAsync2("cursor", ["--version"], {
228
+ timeout: 5e3
229
+ });
230
+ version = stdout.trim().split("\n")[0];
231
+ } catch {
188
232
  }
189
- this.adapters.set(adapter.id, adapter);
233
+ return { id: this.id, name: this.name, version, capabilities: this.getCapabilities() };
190
234
  }
191
- /** Unregister an adapter, disposing it in the process. */
192
- async unregister(id) {
193
- const adapter = this.adapters.get(id);
194
- if (adapter) {
195
- await adapter.dispose();
196
- this.adapters.delete(id);
235
+ async isAvailable() {
236
+ try {
237
+ await execFileAsync2("cursor", ["--version"], { timeout: 3e3 });
238
+ return true;
239
+ } catch {
240
+ return false;
197
241
  }
198
242
  }
199
- /** Retrieve an adapter by id. */
200
- get(id) {
201
- return this.adapters.get(id);
243
+ notifyRegistered() {
244
+ this.emit({ type: "harness-connected", harnessId: this.id });
202
245
  }
203
- /** List all registered adapter ids. */
204
- listAll() {
205
- return Array.from(this.adapters.keys());
246
+ notifyUnregistering() {
247
+ this.emit({ type: "harness-disconnected", harnessId: this.id });
206
248
  }
207
- /** List ids of adapters that report isAvailable() === true. */
208
- async listAvailable() {
209
- const results = await Promise.all(
210
- Array.from(this.adapters.entries()).map(async ([id, adapter]) => ({
211
- id,
212
- available: await adapter.isAvailable()
213
- }))
214
- );
215
- return results.filter((r) => r.available).map((r) => r.id);
249
+ async execute(command) {
250
+ try {
251
+ return await this.executeInner(command);
252
+ } catch (err) {
253
+ const message = err instanceof Error ? err.message : String(err);
254
+ this.emit({ type: "error", harnessId: this.id, message });
255
+ return { success: false, command: command.type, message };
256
+ }
216
257
  }
217
- /** Dispose all adapters and clear the registry. */
218
- async disposeAll() {
219
- await Promise.all(Array.from(this.adapters.values()).map((a) => a.dispose()));
220
- this.adapters.clear();
258
+ async executeInner(command) {
259
+ switch (command.type) {
260
+ case "get-status": {
261
+ const available = await this.isAvailable();
262
+ return { success: true, command: command.type, data: { available } };
263
+ }
264
+ case "read-workboard": {
265
+ if (!this.workboardPath) {
266
+ return {
267
+ success: false,
268
+ command: command.type,
269
+ message: "REVEALUI_WORKBOARD_PATH is not set"
270
+ };
271
+ }
272
+ const manager = new WorkboardManager(this.workboardPath);
273
+ const state = await manager.readAsync();
274
+ return { success: true, command: command.type, data: state };
275
+ }
276
+ case "update-workboard": {
277
+ if (!this.workboardPath) {
278
+ return {
279
+ success: false,
280
+ command: command.type,
281
+ message: "REVEALUI_WORKBOARD_PATH is not set"
282
+ };
283
+ }
284
+ const manager = new WorkboardManager(this.workboardPath);
285
+ manager.updateSession(command.sessionId, {
286
+ ...command.task !== void 0 && { task: command.task },
287
+ ...command.files !== void 0 && { files: command.files.join(", ") },
288
+ updated: `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 16)}Z`
289
+ });
290
+ return { success: true, command: command.type };
291
+ }
292
+ default: {
293
+ return {
294
+ success: false,
295
+ command: command.type,
296
+ message: `Command not supported by ${this.name}`
297
+ };
298
+ }
299
+ }
300
+ }
301
+ onEvent(handler) {
302
+ this.eventHandlers.add(handler);
303
+ return () => this.eventHandlers.delete(handler);
304
+ }
305
+ async dispose() {
306
+ this.eventHandlers.clear();
307
+ }
308
+ emit(event) {
309
+ for (const handler of this.eventHandlers) {
310
+ try {
311
+ handler(event);
312
+ } catch {
313
+ }
314
+ }
221
315
  }
222
316
  };
223
317
 
318
+ // src/config/config-sync.ts
319
+ import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs";
320
+ import { dirname } from "path";
321
+
224
322
  // src/config/harness-config-paths.ts
225
323
  import { homedir } from "os";
226
324
  import { join } from "path";
227
325
  var HOME = homedir();
228
- var SSD_BASE = process.env.REVEALUI_SSD_PATH ?? "/mnt/e/.revealui";
326
+ var REVEALUI_ROOT = process.env.REVEALUI_ROOT ?? join(HOME, ".revealui");
229
327
  var LOCAL_CONFIG_PATHS = {
230
328
  "claude-code": join(HOME, ".claude", "settings.json"),
231
329
  cursor: join(HOME, ".cursor", "settings.json"),
232
330
  copilot: join(HOME, ".config", "github-copilot", "hosts.json")
233
331
  };
234
- var SSD_CONFIG_FILES = {
332
+ var ROOT_CONFIG_FILES = {
235
333
  "claude-code": "settings.json",
236
334
  cursor: "settings.json",
237
335
  copilot: "hosts.json"
@@ -239,22 +337,20 @@ var SSD_CONFIG_FILES = {
239
337
  function getLocalConfigPath(harnessId) {
240
338
  return LOCAL_CONFIG_PATHS[harnessId];
241
339
  }
242
- function getSsdConfigPath(harnessId, ssdBase = SSD_BASE) {
243
- const file = SSD_CONFIG_FILES[harnessId];
340
+ function getRootConfigPath(harnessId, root = REVEALUI_ROOT) {
341
+ const file = ROOT_CONFIG_FILES[harnessId];
244
342
  if (!file) return void 0;
245
- return join(ssdBase, "harness-configs", harnessId, file);
343
+ return join(root, "harness-configs", harnessId, file);
246
344
  }
247
345
  function getConfigurableHarnesses() {
248
346
  return Object.keys(LOCAL_CONFIG_PATHS);
249
347
  }
250
348
 
251
349
  // src/config/config-sync.ts
252
- import { copyFileSync, existsSync, mkdirSync, readFileSync } from "fs";
253
- import { dirname } from "path";
254
- function syncConfig(harnessId, direction, ssdBase) {
350
+ function syncConfig(harnessId, direction, root) {
255
351
  const localPath = getLocalConfigPath(harnessId);
256
- const ssdPath = getSsdConfigPath(harnessId, ssdBase);
257
- if (!(localPath && ssdPath)) {
352
+ const rootPath = getRootConfigPath(harnessId, root);
353
+ if (!(localPath && rootPath)) {
258
354
  return {
259
355
  success: false,
260
356
  harnessId,
@@ -264,12 +360,13 @@ function syncConfig(harnessId, direction, ssdBase) {
264
360
  }
265
361
  try {
266
362
  if (direction === "pull") {
267
- if (!existsSync(ssdPath)) {
268
- return { success: false, harnessId, direction, message: `SSD config not found: ${ssdPath}` };
363
+ if (!existsSync(rootPath)) {
364
+ return { success: false, harnessId, direction, message: `Root config not found: ${rootPath}` };
269
365
  }
270
366
  mkdirSync(dirname(localPath), { recursive: true });
271
- copyFileSync(ssdPath, localPath);
272
- return { success: true, harnessId, direction, message: `Pulled ${ssdPath} \u2192 ${localPath}` };
367
+ backupIfExists(localPath);
368
+ copyFileSync(rootPath, localPath);
369
+ return { success: true, harnessId, direction, message: `Pulled ${rootPath} \u2192 ${localPath}` };
273
370
  } else {
274
371
  if (!existsSync(localPath)) {
275
372
  return {
@@ -279,9 +376,10 @@ function syncConfig(harnessId, direction, ssdBase) {
279
376
  message: `Local config not found: ${localPath}`
280
377
  };
281
378
  }
282
- mkdirSync(dirname(ssdPath), { recursive: true });
283
- copyFileSync(localPath, ssdPath);
284
- return { success: true, harnessId, direction, message: `Pushed ${localPath} \u2192 ${ssdPath}` };
379
+ mkdirSync(dirname(rootPath), { recursive: true });
380
+ backupIfExists(rootPath);
381
+ copyFileSync(localPath, rootPath);
382
+ return { success: true, harnessId, direction, message: `Pushed ${localPath} \u2192 ${rootPath}` };
285
383
  }
286
384
  } catch (err) {
287
385
  return {
@@ -292,32 +390,130 @@ function syncConfig(harnessId, direction, ssdBase) {
292
390
  };
293
391
  }
294
392
  }
295
- function diffConfig(harnessId, ssdBase) {
393
+ function diffConfig(harnessId, root) {
296
394
  const localPath = getLocalConfigPath(harnessId);
297
- const ssdPath = getSsdConfigPath(harnessId, ssdBase);
395
+ const rootPath = getRootConfigPath(harnessId, root);
298
396
  const localExists = !!localPath && existsSync(localPath);
299
- const ssdExists = !!ssdPath && existsSync(ssdPath);
397
+ const ssdExists = !!rootPath && existsSync(rootPath);
300
398
  if (!(localExists && ssdExists)) {
301
399
  return { harnessId, localExists, ssdExists, identical: false };
302
400
  }
303
401
  try {
304
402
  const localContent = readFileSync(localPath, "utf8");
305
- const ssdContent = readFileSync(ssdPath, "utf8");
403
+ const ssdContent = readFileSync(rootPath, "utf8");
306
404
  return { harnessId, localExists, ssdExists, identical: localContent === ssdContent };
307
405
  } catch {
308
406
  return { harnessId, localExists, ssdExists, identical: false };
309
407
  }
310
408
  }
409
+ function syncAllConfigs(direction, root) {
410
+ return getConfigurableHarnesses().map((id) => syncConfig(id, direction, root));
411
+ }
412
+ function diffAllConfigs(root) {
413
+ return getConfigurableHarnesses().map((id) => diffConfig(id, root));
414
+ }
415
+ function validateConfigJson(harnessId) {
416
+ const localPath = getLocalConfigPath(harnessId);
417
+ if (!localPath) return `No config path known for harness: ${harnessId}`;
418
+ if (!existsSync(localPath)) return `Config file not found: ${localPath}`;
419
+ try {
420
+ const content = readFileSync(localPath, "utf8");
421
+ JSON.parse(content);
422
+ return null;
423
+ } catch (err) {
424
+ return err instanceof Error ? err.message : String(err);
425
+ }
426
+ }
427
+ function backupIfExists(filePath) {
428
+ try {
429
+ if (existsSync(filePath)) {
430
+ copyFileSync(filePath, `${filePath}.bak`);
431
+ }
432
+ } catch {
433
+ }
434
+ }
435
+
436
+ // src/coordinator.ts
437
+ import { join as join3 } from "path";
438
+
439
+ // src/detection/auto-detector.ts
440
+ async function autoDetectHarnesses(registry) {
441
+ const candidates = [new ClaudeCodeAdapter(), new CursorAdapter(), new CopilotAdapter()];
442
+ const registered = [];
443
+ await Promise.all(
444
+ candidates.map(async (adapter) => {
445
+ try {
446
+ if (await adapter.isAvailable()) {
447
+ registry.register(adapter);
448
+ registered.push(adapter.id);
449
+ } else {
450
+ await adapter.dispose();
451
+ }
452
+ } catch {
453
+ await adapter.dispose();
454
+ }
455
+ })
456
+ );
457
+ return registered;
458
+ }
459
+
460
+ // src/registry/harness-registry.ts
461
+ var HarnessRegistry = class {
462
+ adapters = /* @__PURE__ */ new Map();
463
+ /** Register an adapter. Throws if an adapter with the same id already exists. */
464
+ register(adapter) {
465
+ if (this.adapters.has(adapter.id)) {
466
+ throw new Error(`Harness adapter already registered: ${adapter.id}`);
467
+ }
468
+ this.adapters.set(adapter.id, adapter);
469
+ adapter.notifyRegistered?.();
470
+ }
471
+ /** Unregister an adapter, disposing it in the process. */
472
+ async unregister(id) {
473
+ const adapter = this.adapters.get(id);
474
+ if (adapter) {
475
+ adapter.notifyUnregistering?.();
476
+ await adapter.dispose();
477
+ this.adapters.delete(id);
478
+ }
479
+ }
480
+ /** Retrieve an adapter by id. */
481
+ get(id) {
482
+ return this.adapters.get(id);
483
+ }
484
+ /** List all registered adapter ids. */
485
+ listAll() {
486
+ return Array.from(this.adapters.keys());
487
+ }
488
+ /** List ids of adapters that report isAvailable() === true. */
489
+ async listAvailable() {
490
+ const results = await Promise.all(
491
+ Array.from(this.adapters.entries()).map(async ([id, adapter]) => ({
492
+ id,
493
+ available: await adapter.isAvailable()
494
+ }))
495
+ );
496
+ return results.filter((r) => r.available).map((r) => r.id);
497
+ }
498
+ /** Dispose all adapters and clear the registry. */
499
+ async disposeAll() {
500
+ await Promise.all(Array.from(this.adapters.values()).map((a) => a.dispose()));
501
+ this.adapters.clear();
502
+ }
503
+ };
504
+
505
+ // src/server/rpc-server.ts
506
+ import { createServer } from "net";
311
507
 
312
508
  // src/detection/process-detector.ts
313
- import { execFile as execFile2 } from "child_process";
509
+ import { execFile as execFile3 } from "child_process";
314
510
  import { readdir } from "fs/promises";
315
511
  import { join as join2 } from "path";
316
- import { promisify as promisify2 } from "util";
317
- var execFileAsync2 = promisify2(execFile2);
512
+ import { promisify as promisify3 } from "util";
513
+ var execFileAsync3 = promisify3(execFile3);
318
514
  async function findProcesses(pattern) {
319
515
  try {
320
- const { stdout } = await execFileAsync2("pgrep", ["-a", pattern], { timeout: 3e3 });
516
+ const { stdout } = await execFileAsync3("pgrep", ["-a", pattern], { timeout: 3e3 });
321
517
  return stdout.trim().split("\n").filter(Boolean).map((line) => {
322
518
  const spaceIdx = line.indexOf(" ");
323
519
  const pid = parseInt(line.slice(0, spaceIdx), 10);
@@ -367,7 +563,6 @@ async function findClaudeCodeSockets() {
367
563
  }
368
564
 
369
565
  // src/server/rpc-server.ts
370
- import { createServer } from "net";
371
566
  var ERR_PARSE = -32700;
372
567
  var ERR_INVALID_PARAMS = -32602;
373
568
  var ERR_METHOD_NOT_FOUND = -32601;
@@ -392,6 +587,7 @@ var RpcServer = class {
392
587
  });
393
588
  }
394
589
  server = createServer();
590
+ healthCheckFn = null;
395
591
  handleLine(line, reply) {
396
592
  let req;
397
593
  try {
@@ -492,6 +688,17 @@ var RpcServer = class {
492
688
  const processes = await findHarnessProcesses(harnessId);
493
689
  return { jsonrpc: "2.0", id, result: processes };
494
690
  }
691
+ case "harness.health": {
692
+ if (!this.healthCheckFn) {
693
+ return {
694
+ jsonrpc: "2.0",
695
+ id,
696
+ error: { code: ERR_INTERNAL, message: "Health check not configured" }
697
+ };
698
+ }
699
+ const health = await this.healthCheckFn();
700
+ return { jsonrpc: "2.0", id, result: health };
701
+ }
495
702
  default:
496
703
  return {
497
704
  jsonrpc: "2.0",
@@ -500,6 +707,10 @@ var RpcServer = class {
500
707
  };
501
708
  }
502
709
  }
710
+ /** Set the health check function (called by coordinator after construction). */
711
+ setHealthCheck(fn) {
712
+ this.healthCheckFn = fn;
713
+ }
503
714
  start() {
504
715
  return new Promise((resolve, reject) => {
505
716
  this.server.listen(this.socketPath, () => resolve());
@@ -512,7 +723,6 @@ var RpcServer = class {
512
723
  };
513
724
 
514
725
  // src/coordinator.ts
515
- import { join as join3 } from "path";
516
726
  var HarnessCoordinator = class {
517
727
  constructor(options) {
518
728
  this.options = options;
@@ -544,6 +754,7 @@ var HarnessCoordinator = class {
544
754
  });
545
755
  const socketPath = this.options.socketPath ?? join3(process.env.HOME ?? "/tmp", ".local", "share", "revealui", "harness.sock");
546
756
  this.rpcServer = new RpcServer(this.registry, socketPath);
757
+ this.rpcServer.setHealthCheck(() => this.healthCheck());
547
758
  await this.rpcServer.start();
548
759
  }
549
760
  async stop() {
@@ -573,23 +784,92 @@ var HarnessCoordinator = class {
573
784
  registerAdapter(adapter) {
574
785
  this.registry.register(adapter);
575
786
  }
787
+ /** Run a health check across all registered harnesses and the workboard. */
788
+ async healthCheck() {
789
+ const diagnostics = [];
790
+ const STALE_MS = 4 * 60 * 60 * 1e3;
791
+ const allIds = this.registry.listAll();
792
+ const registeredHarnesses = await Promise.all(
793
+ allIds.map(async (harnessId) => {
794
+ const adapter = this.registry.get(harnessId);
795
+ let available = false;
796
+ try {
797
+ available = adapter ? await adapter.isAvailable() : false;
798
+ } catch (err) {
799
+ diagnostics.push(
800
+ `${harnessId}: availability check failed \u2014 ${err instanceof Error ? err.message : String(err)}`
801
+ );
802
+ }
803
+ if (!available) diagnostics.push(`${harnessId}: not available`);
804
+ return { harnessId, available };
805
+ })
806
+ );
807
+ let readable = false;
808
+ let sessionCount = 0;
809
+ const staleSessionIds = [];
810
+ try {
811
+ const state = this.workboard.read();
812
+ readable = true;
813
+ sessionCount = state.sessions.length;
814
+ const now = Date.now();
815
+ for (const s of state.sessions) {
816
+ const ts = Date.parse(s.updated);
817
+ if (!Number.isNaN(ts) && now - ts > STALE_MS) {
818
+ staleSessionIds.push(s.id);
819
+ }
820
+ }
821
+ if (staleSessionIds.length > 0) {
822
+ diagnostics.push(`Stale sessions: ${staleSessionIds.join(", ")}`);
823
+ }
824
+ } catch (err) {
825
+ diagnostics.push(
826
+ `Workboard unreadable: ${err instanceof Error ? err.message : String(err)}`
827
+ );
828
+ }
829
+ const healthy = registeredHarnesses.some((h) => h.available) && readable && staleSessionIds.length === 0;
830
+ return {
831
+ healthy,
832
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
833
+ registeredHarnesses,
834
+ workboard: { readable, sessionCount, staleSessionIds },
835
+ diagnostics
836
+ };
837
+ }
576
838
  };
577
839
 
840
+ // src/index.ts
841
+ async function checkHarnessesLicense() {
842
+ await initializeLicense();
843
+ if (!isFeatureEnabled("ai")) {
844
+ logger.warn(
845
+ "[@revealui/harnesses] AI harness integration requires a Pro or Enterprise license. Visit https://revealui.com/pricing for details.",
846
+ { feature: "ai" }
847
+ );
848
+ return false;
849
+ }
850
+ return true;
851
+ }
852
+
578
853
  export {
579
854
  ClaudeCodeAdapter,
580
855
  CopilotAdapter,
856
+ CursorAdapter,
581
857
  autoDetectHarnesses,
582
858
  HarnessRegistry,
583
859
  getLocalConfigPath,
584
- getSsdConfigPath,
860
+ getRootConfigPath,
585
861
  getConfigurableHarnesses,
586
862
  syncConfig,
587
863
  diffConfig,
864
+ syncAllConfigs,
865
+ diffAllConfigs,
866
+ validateConfigJson,
588
867
  findProcesses,
589
868
  findHarnessProcesses,
590
869
  findAllHarnessProcesses,
591
870
  findClaudeCodeSockets,
592
871
  RpcServer,
593
- HarnessCoordinator
872
+ HarnessCoordinator,
873
+ checkHarnessesLicense
594
874
  };
595
- //# sourceMappingURL=chunk-BDA7D725.js.map
875
+ //# sourceMappingURL=chunk-MO2EXBUG.js.map