@markus-global/cli 0.7.14 → 0.8.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/dist/markus.mjs CHANGED
@@ -3791,6 +3791,34 @@ var init_model_catalog = __esm({
3791
3791
  }
3792
3792
  });
3793
3793
 
3794
+ // ../shared/dist/types/license.js
3795
+ var PLAN_LIMITS, ENTERPRISE_FEATURES;
3796
+ var init_license = __esm({
3797
+ "../shared/dist/types/license.js"() {
3798
+ "use strict";
3799
+ PLAN_LIMITS = {
3800
+ free: {
3801
+ maxTeams: 1,
3802
+ maxToolCallsPerDay: 500,
3803
+ maxUsers: 1
3804
+ },
3805
+ enterprise: {
3806
+ maxTeams: -1,
3807
+ maxToolCallsPerDay: -1,
3808
+ maxUsers: -1
3809
+ }
3810
+ };
3811
+ ENTERPRISE_FEATURES = [
3812
+ "multi_user",
3813
+ "unlimited_teams",
3814
+ "unlimited_tools",
3815
+ "sso",
3816
+ "audit_enhanced",
3817
+ "multi_instance"
3818
+ ];
3819
+ }
3820
+ });
3821
+
3794
3822
  // ../shared/dist/utils/config.js
3795
3823
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3796
3824
  import { resolve, join } from "node:path";
@@ -4515,6 +4543,7 @@ __export(dist_exports, {
4515
4543
  CognitiveDepth: () => CognitiveDepth,
4516
4544
  DELIBERATION_ALLOWED_TOOLS: () => DELIBERATION_ALLOWED_TOOLS,
4517
4545
  DELIVERABLE_TITLE_CHARS: () => DELIVERABLE_TITLE_CHARS,
4546
+ ENTERPRISE_FEATURES: () => ENTERPRISE_FEATURES,
4518
4547
  ENTITY_COMMENTS_DEFAULT: () => ENTITY_COMMENTS_DEFAULT,
4519
4548
  HEARTBEAT_DAILY_LOG_CHARS: () => HEARTBEAT_DAILY_LOG_CHARS,
4520
4549
  HEARTBEAT_MIN_INITIAL_DELAY_MS: () => HEARTBEAT_MIN_INITIAL_DELAY_MS,
@@ -4533,6 +4562,7 @@ __export(dist_exports, {
4533
4562
  MEMORY_MD_SECTION_MAX_CHARS: () => MEMORY_MD_SECTION_MAX_CHARS,
4534
4563
  MEMORY_MD_TOTAL_MAX_CHARS: () => MEMORY_MD_TOTAL_MAX_CHARS,
4535
4564
  MailboxPriorityLevel: () => MailboxPriorityLevel,
4565
+ PLAN_LIMITS: () => PLAN_LIMITS,
4536
4566
  PREEMPT_REQUEUE_DELAY_MS: () => PREEMPT_REQUEUE_DELAY_MS,
4537
4567
  PRIORITY_LABELS: () => PRIORITY_LABELS,
4538
4568
  PROMPT_DEP_DESC_CHARS: () => PROMPT_DEP_DESC_CHARS,
@@ -4653,6 +4683,7 @@ var init_dist = __esm({
4653
4683
  init_mailbox();
4654
4684
  init_cognitive();
4655
4685
  init_model_catalog();
4686
+ init_license();
4656
4687
  init_config();
4657
4688
  init_logger();
4658
4689
  init_id();
@@ -41975,14 +42006,14 @@ var require_turndown_cjs = __commonJS({
41975
42006
  } else if (node.nodeType === 1) {
41976
42007
  replacement = replacementForNode.call(self, node);
41977
42008
  }
41978
- return join33(output, replacement);
42009
+ return join35(output, replacement);
41979
42010
  }, "");
41980
42011
  }
41981
42012
  function postProcess(output) {
41982
42013
  var self = this;
41983
42014
  this.rules.forEach(function(rule) {
41984
42015
  if (typeof rule.append === "function") {
41985
- output = join33(output, rule.append(self.options));
42016
+ output = join35(output, rule.append(self.options));
41986
42017
  }
41987
42018
  });
41988
42019
  return output.replace(/^[\t\r\n]+/, "").replace(/[\t\r\n\s]+$/, "");
@@ -41994,7 +42025,7 @@ var require_turndown_cjs = __commonJS({
41994
42025
  if (whitespace2.leading || whitespace2.trailing) content = content.trim();
41995
42026
  return whitespace2.leading + rule.replacement(content, node, this.options) + whitespace2.trailing;
41996
42027
  }
41997
- function join33(output, replacement) {
42028
+ function join35(output, replacement) {
41998
42029
  var s1 = trimTrailingNewlines(output);
41999
42030
  var s2 = trimLeadingNewlines(replacement);
42000
42031
  var nls = Math.max(output.length - s1.length, replacement.length - s2.length);
@@ -46805,6 +46836,7 @@ var init_agent2 = __esm({
46805
46836
  auditCallback;
46806
46837
  escalationCallback;
46807
46838
  approvalCallback;
46839
+ toolCallLimitChecker;
46808
46840
  tasksFetcher;
46809
46841
  consecutiveFailures = 0;
46810
46842
  metricsCollector;
@@ -48591,6 +48623,9 @@ ${instructions}
48591
48623
  setEscalationCallback(cb) {
48592
48624
  this.escalationCallback = cb;
48593
48625
  }
48626
+ setToolCallLimitChecker(cb) {
48627
+ this.toolCallLimitChecker = cb;
48628
+ }
48594
48629
  setStateChangeCallback(cb) {
48595
48630
  this.stateChangeCallback = cb;
48596
48631
  }
@@ -48738,16 +48773,16 @@ ${instructions}
48738
48773
  const toolNames = /* @__PURE__ */ new Set();
48739
48774
  const errors = [];
48740
48775
  let lastText = "";
48741
- for (const log89 of logs) {
48742
- if (log89.type === "tool_start") {
48743
- const name = log89.metadata?.toolName ?? "";
48776
+ for (const log91 of logs) {
48777
+ if (log91.type === "tool_start") {
48778
+ const name = log91.metadata?.toolName ?? "";
48744
48779
  if (name)
48745
48780
  toolNames.add(name);
48746
48781
  }
48747
- if (log89.type === "error")
48748
- errors.push(log89.content.slice(0, 100));
48749
- if (log89.type === "text")
48750
- lastText = log89.content.slice(0, 200);
48782
+ if (log91.type === "error")
48783
+ errors.push(log91.content.slice(0, 100));
48784
+ if (log91.type === "text")
48785
+ lastText = log91.content.slice(0, 200);
48751
48786
  }
48752
48787
  const parts = [];
48753
48788
  if (toolNames.size > 0)
@@ -51138,6 +51173,15 @@ ${body}${contextSuffix}`;
51138
51173
  error: beforeResult.reason ?? "Blocked by tool hook"
51139
51174
  });
51140
51175
  }
51176
+ if (this.toolCallLimitChecker) {
51177
+ const limitResult = this.toolCallLimitChecker();
51178
+ if (!limitResult.allowed) {
51179
+ return JSON.stringify({
51180
+ status: "denied",
51181
+ error: limitResult.reason ?? "Tool call limit reached"
51182
+ });
51183
+ }
51184
+ }
51141
51185
  const baseArgs = beforeResult.modifiedArgs ?? toolCall.arguments;
51142
51186
  const effectiveArgs = sessionId ? { ...baseArgs, _browserSessionId: sessionId } : baseArgs;
51143
51187
  let lastError;
@@ -57929,6 +57973,7 @@ var init_agent_manager = __esm({
57929
57973
  agentAuditCallback;
57930
57974
  escalationHandler;
57931
57975
  approvalHandler;
57976
+ toolCallLimitChecker;
57932
57977
  stateChangeHandler;
57933
57978
  /** Grace timers for releasing scoped MCP processes after agent goes idle */
57934
57979
  mcpReleaseTimers = /* @__PURE__ */ new Map();
@@ -58910,6 +58955,9 @@ Known issues: ${knownIssues}` : ""}`
58910
58955
  const ah = this.approvalHandler;
58911
58956
  agent.setApprovalCallback(async (req) => ah(id, req));
58912
58957
  }
58958
+ if (this.toolCallLimitChecker) {
58959
+ agent.setToolCallLimitChecker(this.toolCallLimitChecker);
58960
+ }
58913
58961
  agent.setStateChangeCallback(this.buildStateChangeCallback());
58914
58962
  if (this.activityCallbacks) {
58915
58963
  agent.setActivityCallbacks(this.activityCallbacks);
@@ -59544,6 +59592,9 @@ Known issues: ${knownIssues}` : ""}`
59544
59592
  const ah = this.approvalHandler;
59545
59593
  agent.setApprovalCallback(async (req) => ah(id, req));
59546
59594
  }
59595
+ if (this.toolCallLimitChecker) {
59596
+ agent.setToolCallLimitChecker(this.toolCallLimitChecker);
59597
+ }
59547
59598
  agent.setStateChangeCallback(this.buildStateChangeCallback());
59548
59599
  if (this.activityCallbacks) {
59549
59600
  agent.setActivityCallbacks(this.activityCallbacks);
@@ -59690,6 +59741,12 @@ Known issues: ${knownIssues}` : ""}`
59690
59741
  agent.setApprovalCallback(async (req) => handler4(id, req));
59691
59742
  }
59692
59743
  }
59744
+ setToolCallLimitChecker(checker) {
59745
+ this.toolCallLimitChecker = checker;
59746
+ for (const [, agent] of this.agents) {
59747
+ agent.setToolCallLimitChecker(checker);
59748
+ }
59749
+ }
59693
59750
  /**
59694
59751
  * Build the combined state-change callback for an agent. Handles:
59695
59752
  * 1. MCP scoped-process lifecycle (release on idle, cancel release on working)
@@ -63957,7 +64014,7 @@ var init_external_gateway = __esm({
63957
64014
  return rows.length;
63958
64015
  }
63959
64016
  async register(request) {
63960
- const { externalAgentId, agentName, orgId: orgId2, capabilities = [], platform: platform4, platformConfig, agentCardUrl, openClawConfig } = request;
64017
+ const { externalAgentId, agentName, orgId: orgId2, capabilities = [], platform: platform5, platformConfig, agentCardUrl, openClawConfig } = request;
63961
64018
  if (!externalAgentId || !agentName || !orgId2) {
63962
64019
  throw new GatewayError("Missing required fields: externalAgentId, agentName, orgId", 400);
63963
64020
  }
@@ -63984,7 +64041,7 @@ var init_external_gateway = __esm({
63984
64041
  agentName,
63985
64042
  orgId: orgId2,
63986
64043
  capabilities,
63987
- platform: platform4 ?? (openClawConfig ? "openclaw" : void 0),
64044
+ platform: platform5 ?? (openClawConfig ? "openclaw" : void 0),
63988
64045
  platformConfig: platformConfig ?? openClawConfig,
63989
64046
  agentCardUrl,
63990
64047
  openClawConfig,
@@ -76781,8 +76838,8 @@ var require_CronFileParser = __commonJS({
76781
76838
  * @throws If file cannot be read
76782
76839
  */
76783
76840
  static parseFileSync(filePath) {
76784
- const { readFileSync: readFileSync29 } = __require("fs");
76785
- const data = readFileSync29(filePath, "utf8");
76841
+ const { readFileSync: readFileSync31 } = __require("fs");
76842
+ const data = readFileSync31(filePath, "utf8");
76786
76843
  return _CronFileParser.#parseContent(data);
76787
76844
  }
76788
76845
  /**
@@ -82111,6 +82168,8 @@ var init_api_server = __esm({
82111
82168
  hitlService;
82112
82169
  billingService;
82113
82170
  auditService;
82171
+ licenseService;
82172
+ telemetryService;
82114
82173
  storage;
82115
82174
  llmRouter;
82116
82175
  markusConfigPath;
@@ -82130,7 +82189,40 @@ var init_api_server = __esm({
82130
82189
  remoteAgent;
82131
82190
  remoteAgentFactory;
82132
82191
  modelCatalog;
82133
- // Custom group chats are now persisted in SQLite via storage.groupChatRepo
82192
+ /** Aggregate today's tool calls from all agents' persisted metrics (the single source of truth) */
82193
+ getToolCallsTodayFromAgents() {
82194
+ try {
82195
+ const agentManager = this.orgService.getAgentManager();
82196
+ const allAgents = agentManager.listAgents();
82197
+ let total = 0;
82198
+ for (const a of allAgents) {
82199
+ try {
82200
+ const agent = agentManager.getAgent(a.id);
82201
+ total += agent.getUsageStats().toolCallsToday;
82202
+ } catch {
82203
+ }
82204
+ }
82205
+ return total;
82206
+ } catch {
82207
+ return 0;
82208
+ }
82209
+ }
82210
+ /** fetch that follows redirects while preserving the Authorization header */
82211
+ async hubFetch(url, init) {
82212
+ let currentUrl = url;
82213
+ for (let i = 0; i < 3; i++) {
82214
+ const res = await fetch(currentUrl, { ...init, redirect: "manual" });
82215
+ if (res.status >= 300 && res.status < 400) {
82216
+ const location = res.headers.get("location");
82217
+ if (!location)
82218
+ return res;
82219
+ currentUrl = new URL(location, currentUrl).href;
82220
+ continue;
82221
+ }
82222
+ return res;
82223
+ }
82224
+ return fetch(currentUrl, init);
82225
+ }
82134
82226
  constructor(orgService, taskService, port = 8056) {
82135
82227
  this.orgService = orgService;
82136
82228
  this.taskService = taskService;
@@ -82339,7 +82431,7 @@ ${cleanText}`,
82339
82431
  const headers = { "Content-Type": "application/json" };
82340
82432
  if (token)
82341
82433
  headers["Authorization"] = `Bearer ${token}`;
82342
- const res = await fetch(`${hubUrl}/api/items${qs ? `?${qs}` : ""}`, { headers });
82434
+ const res = await self.hubFetch(`${hubUrl}/api/items${qs ? `?${qs}` : ""}`, { headers });
82343
82435
  if (!res.ok)
82344
82436
  throw new Error(`Hub search failed: ${res.status}`);
82345
82437
  const data = await res.json();
@@ -82359,7 +82451,7 @@ ${cleanText}`,
82359
82451
  if (!token)
82360
82452
  throw new Error("Hub token not configured. Please login to Markus Hub first.");
82361
82453
  const headers = { "Content-Type": "application/json", "Authorization": `Bearer ${token}` };
82362
- const res = await fetch(`${hubUrl}/api/items/${itemId}/download`, { method: "POST", headers });
82454
+ const res = await self.hubFetch(`${hubUrl}/api/items/${itemId}/download`, { method: "POST", headers });
82363
82455
  if (!res.ok)
82364
82456
  throw new Error(`Hub download failed: ${res.status}`);
82365
82457
  const data = await res.json();
@@ -82393,6 +82485,12 @@ ${cleanText}`,
82393
82485
  setBillingService(service) {
82394
82486
  this.billingService = service;
82395
82487
  }
82488
+ setLicenseService(service) {
82489
+ this.licenseService = service;
82490
+ }
82491
+ setTelemetryService(service) {
82492
+ this.telemetryService = service;
82493
+ }
82396
82494
  setAuditService(service) {
82397
82495
  this.auditService = service;
82398
82496
  }
@@ -83145,12 +83243,18 @@ ${cleanText}`,
83145
83243
  }
83146
83244
  if (path === "/api/auth/status" && req.method === "GET") {
83147
83245
  if (!this.storage || !this.authEnabled) {
83148
- this.json(res, 200, { initialized: true });
83246
+ this.json(res, 200, { initialized: true, hasOwner: true, hasMultipleUsers: false });
83149
83247
  return;
83150
83248
  }
83151
83249
  const allUsers = await this.storage.userRepo.listByOrg("default");
83152
- const hasRealUsers = allUsers.some((u) => u.passwordHash && u.email !== "admin@markus.local");
83153
- this.json(res, 200, { initialized: hasRealUsers });
83250
+ const realUsers = allUsers.filter((u) => (u.passwordHash || u.hubUserId) && u.email !== "admin@markus.local");
83251
+ const hasOwner = realUsers.some((u) => u.role === "owner");
83252
+ const hasMultipleUsers = realUsers.length > 1;
83253
+ this.json(res, 200, {
83254
+ initialized: realUsers.length > 0,
83255
+ hasOwner,
83256
+ hasMultipleUsers
83257
+ });
83154
83258
  return;
83155
83259
  }
83156
83260
  if (path === "/api/auth/init" && req.method === "POST") {
@@ -83252,6 +83356,135 @@ ${cleanText}`,
83252
83356
  });
83253
83357
  return;
83254
83358
  }
83359
+ if (path === "/api/auth/hub-login" && req.method === "POST") {
83360
+ if (!this.storage) {
83361
+ this.json(res, 503, { error: "Storage not available" });
83362
+ return;
83363
+ }
83364
+ const body = await this.readBody(req);
83365
+ const hubToken = body["hubToken"];
83366
+ const hubUser = body["hubUser"];
83367
+ if (!hubToken || !hubUser?.id) {
83368
+ this.json(res, 400, { error: "hubToken and hubUser are required" });
83369
+ return;
83370
+ }
83371
+ let verifiedUser = null;
83372
+ try {
83373
+ const verifyRes = await this.hubFetch(`${this.hubUrl}/api/auth/me`, {
83374
+ headers: { "Authorization": `Bearer ${hubToken}` }
83375
+ });
83376
+ if (verifyRes.ok) {
83377
+ const verifyData = await verifyRes.json();
83378
+ if (verifyData.user && verifyData.user.id === hubUser.id) {
83379
+ verifiedUser = verifyData.user;
83380
+ } else {
83381
+ log61.warn("Hub token user mismatch", { expected: hubUser.id, got: verifyData.user?.id });
83382
+ }
83383
+ } else {
83384
+ log61.warn("Hub /api/auth/me returned non-OK", { status: verifyRes.status, hubUrl: this.hubUrl });
83385
+ }
83386
+ } catch (e) {
83387
+ log61.warn("Hub token verification failed, proceeding with client-supplied data", { error: e.message, hubUrl: this.hubUrl });
83388
+ }
83389
+ if (!verifiedUser) {
83390
+ verifiedUser = { id: hubUser.id, username: hubUser.username, email: hubUser.email, displayName: hubUser.displayName, avatarUrl: hubUser.avatarUrl };
83391
+ }
83392
+ const rawEmail = (verifiedUser.email ?? hubUser.email ?? "").trim().toLowerCase();
83393
+ const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(rawEmail) ? rawEmail : "";
83394
+ const name = verifiedUser.displayName || verifiedUser.username || hubUser.displayName || hubUser.username || email && email.split("@")[0] || "User";
83395
+ const avatarUrl = verifiedUser.avatarUrl ?? hubUser.avatarUrl ?? null;
83396
+ let userRow = this.storage.userRepo.findByHubUserId(hubUser.id);
83397
+ let isFirstLogin = false;
83398
+ if (!userRow && email) {
83399
+ userRow = this.storage.userRepo.findByEmail(email);
83400
+ if (userRow) {
83401
+ this.storage.userRepo.updateHubUserId(userRow.id, hubUser.id, hubUser.username);
83402
+ if (avatarUrl && !userRow.avatarUrl) {
83403
+ this.storage.userRepo.updateAvatarUrl(userRow.id, avatarUrl);
83404
+ }
83405
+ }
83406
+ }
83407
+ if (!userRow) {
83408
+ const allUsers = await this.storage.userRepo.listByOrg("default");
83409
+ const placeholder = allUsers.find((u) => u.role === "owner" && u.email === "admin@markus.local");
83410
+ const hasRealOwner = allUsers.some((u) => u.role === "owner" && (u.passwordHash || u.hubUserId) && u.email !== "admin@markus.local");
83411
+ if (placeholder && !hasRealOwner) {
83412
+ this.storage.userRepo.updateProfile(placeholder.id, { name, email: email || void 0, avatarUrl });
83413
+ this.storage.userRepo.updateHubUserId(placeholder.id, hubUser.id, hubUser.username);
83414
+ userRow = this.storage.userRepo.findById(placeholder.id);
83415
+ isFirstLogin = true;
83416
+ } else if (!hasRealOwner) {
83417
+ const userId2 = userId();
83418
+ this.storage.userRepo.create({
83419
+ id: userId2,
83420
+ orgId: "default",
83421
+ name,
83422
+ email: email || void 0,
83423
+ role: "owner",
83424
+ hubUserId: hubUser.id,
83425
+ avatarUrl: avatarUrl ?? void 0
83426
+ });
83427
+ this.storage.userRepo.updateHubUserId(userId2, hubUser.id, hubUser.username);
83428
+ userRow = this.storage.userRepo.findById(userId2);
83429
+ isFirstLogin = true;
83430
+ } else {
83431
+ const realOwners = allUsers.filter((u) => u.role === "owner" && (u.passwordHash || u.hubUserId) && u.email !== "admin@markus.local");
83432
+ if (realOwners.length === 1 && !realOwners[0].hubUserId) {
83433
+ const existingOwner = realOwners[0];
83434
+ this.storage.userRepo.updateProfile(existingOwner.id, { name: existingOwner.name, email: email || existingOwner.email, avatarUrl: avatarUrl ?? existingOwner.avatarUrl });
83435
+ this.storage.userRepo.updateHubUserId(existingOwner.id, hubUser.id, hubUser.username);
83436
+ userRow = this.storage.userRepo.findById(existingOwner.id);
83437
+ log61.info("Hub login: adopted existing owner", { ownerId: existingOwner.id, hubUserId: hubUser.id });
83438
+ } else {
83439
+ this.json(res, 403, { error: "This instance already has an owner. Multi-user requires Enterprise license." });
83440
+ return;
83441
+ }
83442
+ }
83443
+ }
83444
+ if (!userRow) {
83445
+ this.json(res, 500, { error: "Failed to create user" });
83446
+ return;
83447
+ }
83448
+ const hubUsername = verifiedUser.username || hubUser.username;
83449
+ if (hubUsername) {
83450
+ this.storage.userRepo.updateHubUserId(userRow.id, hubUser.id, hubUsername);
83451
+ }
83452
+ const profileUpdates = {};
83453
+ if (name && name !== userRow.name)
83454
+ profileUpdates.name = name;
83455
+ if (email && email !== userRow.email)
83456
+ profileUpdates.email = email;
83457
+ if (avatarUrl && avatarUrl !== userRow.avatarUrl)
83458
+ profileUpdates.avatarUrl = avatarUrl;
83459
+ if (Object.keys(profileUpdates).length > 0) {
83460
+ this.storage.userRepo.updateProfile(userRow.id, profileUpdates);
83461
+ userRow = this.storage.userRepo.findById(userRow.id);
83462
+ }
83463
+ this.orgService.syncHumanIdentity(userRow.id, "default", userRow.name, userRow.role, userRow.email ?? void 0);
83464
+ try {
83465
+ const tokenPath = join24(homedir15(), ".markus", "hub-token");
83466
+ mkdirSync19(dirname8(tokenPath), { recursive: true });
83467
+ writeFileSync17(tokenPath, hubToken, "utf-8");
83468
+ } catch {
83469
+ }
83470
+ const finalUser = userRow;
83471
+ await this.storage.userRepo.updateLastLogin(finalUser.id);
83472
+ const exp = Math.floor(Date.now() / 1e3) + 7 * 24 * 3600;
83473
+ const token = await signToken({ userId: finalUser.id, orgId: "default", role: finalUser.role, exp }, this.jwtSecret);
83474
+ res.setHeader("Set-Cookie", `markus_token=${token}; HttpOnly; SameSite=Strict; Path=/; Max-Age=${7 * 24 * 3600}`);
83475
+ this.json(res, 200, {
83476
+ user: {
83477
+ id: finalUser.id,
83478
+ name: finalUser.name,
83479
+ email: finalUser.email,
83480
+ role: finalUser.role,
83481
+ orgId: finalUser.orgId,
83482
+ avatarUrl: finalUser.avatarUrl ?? void 0
83483
+ },
83484
+ needsOnboarding: isFirstLogin
83485
+ });
83486
+ return;
83487
+ }
83255
83488
  if (path === "/api/auth/logout" && req.method === "POST") {
83256
83489
  res.setHeader("Set-Cookie", "markus_token=; HttpOnly; SameSite=Strict; Path=/; Max-Age=0");
83257
83490
  this.json(res, 200, { ok: true });
@@ -84340,6 +84573,16 @@ ${cleanText}`,
84340
84573
  this.json(res, 400, { error: "name is required" });
84341
84574
  return;
84342
84575
  }
84576
+ if (this.licenseService) {
84577
+ const limits = this.licenseService.getLimits();
84578
+ if (limits.maxTeams > 0) {
84579
+ const existingTeams = await this.orgService.listTeams(orgId2);
84580
+ if (existingTeams.length >= limits.maxTeams) {
84581
+ this.json(res, 403, { error: `Team limit reached (${limits.maxTeams}). Upgrade to Enterprise for unlimited teams.` });
84582
+ return;
84583
+ }
84584
+ }
84585
+ }
84343
84586
  const team = await this.orgService.createTeam(orgId2, name, body["description"]);
84344
84587
  this.ws?.broadcast({
84345
84588
  type: "chat:group_created",
@@ -86470,6 +86713,16 @@ EXPLANATION_END`;
86470
86713
  const authUser = await this.requireAuth(req, res);
86471
86714
  if (!authUser)
86472
86715
  return;
86716
+ if (this.licenseService && this.storage) {
86717
+ const limits = this.licenseService.getLimits();
86718
+ if (limits.maxUsers > 0) {
86719
+ const existingCount = this.storage.userRepo.countByOrg("default");
86720
+ if (existingCount >= limits.maxUsers) {
86721
+ this.json(res, 403, { error: `User limit reached (${limits.maxUsers}). Upgrade to Enterprise for multi-user support.` });
86722
+ return;
86723
+ }
86724
+ }
86725
+ }
86473
86726
  const body = await this.readBody(req);
86474
86727
  const orgId2 = body["orgId"] ?? "default";
86475
86728
  const name = body["name"];
@@ -86750,14 +87003,14 @@ EXPLANATION_END`;
86750
87003
  const installedSkills = new Map((this.skillRegistry?.list() ?? []).map((s2) => [s2.name, s2]));
86751
87004
  const rawManifests = /* @__PURE__ */ new Map();
86752
87005
  try {
86753
- const { readdirSync: readdirSync14, readFileSync: readFileSync29, existsSync: existsSync40 } = await import("node:fs");
87006
+ const { readdirSync: readdirSync14, readFileSync: readFileSync31, existsSync: existsSync42 } = await import("node:fs");
86754
87007
  for (const entry of readdirSync14(builtinDir, { withFileTypes: true })) {
86755
87008
  if (!entry.isDirectory())
86756
87009
  continue;
86757
87010
  const sjPath = resolve15(builtinDir, entry.name, "skill.json");
86758
- if (existsSync40(sjPath)) {
87011
+ if (existsSync42(sjPath)) {
86759
87012
  try {
86760
- rawManifests.set(entry.name, JSON.parse(readFileSync29(sjPath, "utf-8")));
87013
+ rawManifests.set(entry.name, JSON.parse(readFileSync31(sjPath, "utf-8")));
86761
87014
  } catch {
86762
87015
  }
86763
87016
  }
@@ -87184,6 +87437,16 @@ EXPLANATION_END`;
87184
87437
  this.json(res, 500, { error: "BuilderService not initialized" });
87185
87438
  return;
87186
87439
  }
87440
+ if (type === "team" && this.licenseService) {
87441
+ const limits = this.licenseService.getLimits();
87442
+ if (limits.maxTeams > 0) {
87443
+ const existingTeams = await this.orgService.listTeams("default");
87444
+ if (existingTeams.length >= limits.maxTeams) {
87445
+ this.json(res, 403, { error: `Team limit reached (${limits.maxTeams}). Upgrade to Enterprise for unlimited teams.` });
87446
+ return;
87447
+ }
87448
+ }
87449
+ }
87187
87450
  try {
87188
87451
  const result = await this.builderService.installArtifact(type, name);
87189
87452
  this.json(res, 201, result);
@@ -88199,11 +88462,11 @@ EXPLANATION_END`;
88199
88462
  }
88200
88463
  if (path === "/api/templates/teams" && req.method === "GET") {
88201
88464
  try {
88202
- const { readdirSync: readdirSync14, readFileSync: readFileSync29 } = await import("node:fs");
88465
+ const { readdirSync: readdirSync14, readFileSync: readFileSync31 } = await import("node:fs");
88203
88466
  const { resolve: resolve21 } = await import("node:path");
88204
88467
  const teamsDir = resolve21(process.cwd(), "templates", "teams");
88205
88468
  const files = readdirSync14(teamsDir).filter((f) => f.endsWith(".json"));
88206
- const teams = files.map((f) => JSON.parse(readFileSync29(resolve21(teamsDir, f), "utf-8")));
88469
+ const teams = files.map((f) => JSON.parse(readFileSync31(resolve21(teamsDir, f), "utf-8")));
88207
88470
  this.json(res, 200, { templates: teams });
88208
88471
  } catch {
88209
88472
  this.json(res, 200, { templates: [] });
@@ -88244,6 +88507,183 @@ EXPLANATION_END`;
88244
88507
  this.json(res, 200, { usage });
88245
88508
  return;
88246
88509
  }
88510
+ if (path === "/api/license" && req.method === "GET") {
88511
+ const raw = this.licenseService ? this.licenseService.getInfo() : { plan: "free", features: [], limits: { maxTeams: 1, maxToolCallsPerDay: 500, maxUsers: 1 } };
88512
+ const info2 = { ...raw };
88513
+ const authUser = await this.getAuthUser(req);
88514
+ if (authUser && this.storage) {
88515
+ const userRow = this.storage.userRepo.findById(authUser.userId);
88516
+ if (userRow) {
88517
+ if (userRow.hubUserId)
88518
+ info2.hubUserId = userRow.hubUserId;
88519
+ info2.username = userRow.hubUsername || userRow.name || void 0;
88520
+ }
88521
+ }
88522
+ try {
88523
+ const defaultOrg = this.orgService.getDefaultOrganization();
88524
+ const orgId2 = defaultOrg?.id ?? "default";
88525
+ const teams = this.orgService.listTeams(orgId2);
88526
+ const humans = this.orgService.listHumanUsers(orgId2);
88527
+ const todayToolCalls = this.getToolCallsTodayFromAgents();
88528
+ info2.usage = { teams: teams.length, toolCallsToday: todayToolCalls, users: humans.length };
88529
+ } catch {
88530
+ }
88531
+ const hubToken = this.readHubToken();
88532
+ if (hubToken) {
88533
+ try {
88534
+ const meRes = await this.hubFetch(`${this.hubUrl}/api/auth/me`, {
88535
+ headers: { "Authorization": `Bearer ${hubToken}` }
88536
+ });
88537
+ if (meRes.ok) {
88538
+ const meData = await meRes.json();
88539
+ if (meData.defaultOrg) {
88540
+ info2.defaultOrg = meData.defaultOrg;
88541
+ if (!info2.orgId)
88542
+ info2.orgId = meData.defaultOrg.id;
88543
+ if (!info2.orgName)
88544
+ info2.orgName = meData.defaultOrg.name;
88545
+ }
88546
+ if (meData.user?.id && !info2.hubUserId)
88547
+ info2.hubUserId = meData.user.id;
88548
+ }
88549
+ } catch {
88550
+ }
88551
+ }
88552
+ this.json(res, 200, info2);
88553
+ return;
88554
+ }
88555
+ if (path === "/api/license/refresh" && req.method === "POST") {
88556
+ if (!this.licenseService) {
88557
+ this.json(res, 503, { error: "License service not available" });
88558
+ return;
88559
+ }
88560
+ const raw = await this.licenseService.revalidate();
88561
+ if (this.billingService)
88562
+ this.billingService.setOrgPlan("default", this.licenseService.getPlan());
88563
+ const info2 = { ...raw };
88564
+ const authUser = await this.getAuthUser(req);
88565
+ if (authUser && this.storage) {
88566
+ const userRow = this.storage.userRepo.findById(authUser.userId);
88567
+ if (userRow) {
88568
+ if (userRow.hubUserId)
88569
+ info2.hubUserId = userRow.hubUserId;
88570
+ info2.username = userRow.hubUsername || userRow.name || void 0;
88571
+ }
88572
+ }
88573
+ try {
88574
+ const defaultOrg = this.orgService.getDefaultOrganization();
88575
+ const orgId2 = defaultOrg?.id ?? "default";
88576
+ const teams = this.orgService.listTeams(orgId2);
88577
+ const humans = this.orgService.listHumanUsers(orgId2);
88578
+ const todayToolCalls = this.getToolCallsTodayFromAgents();
88579
+ info2.usage = { teams: teams.length, toolCallsToday: todayToolCalls, users: humans.length };
88580
+ } catch {
88581
+ }
88582
+ const hubToken = this.readHubToken();
88583
+ if (hubToken) {
88584
+ try {
88585
+ const meRes = await this.hubFetch(`${this.hubUrl}/api/auth/me`, {
88586
+ headers: { "Authorization": `Bearer ${hubToken}` }
88587
+ });
88588
+ if (meRes.ok) {
88589
+ const meData = await meRes.json();
88590
+ if (meData.defaultOrg) {
88591
+ info2.defaultOrg = meData.defaultOrg;
88592
+ if (!info2.orgId)
88593
+ info2.orgId = meData.defaultOrg.id;
88594
+ if (!info2.orgName)
88595
+ info2.orgName = meData.defaultOrg.name;
88596
+ }
88597
+ if (meData.user?.id && !info2.hubUserId)
88598
+ info2.hubUserId = meData.user.id;
88599
+ }
88600
+ } catch {
88601
+ }
88602
+ }
88603
+ this.json(res, 200, info2);
88604
+ return;
88605
+ }
88606
+ if (path === "/api/license/activate" && req.method === "POST") {
88607
+ const authUser = await this.requireAuth(req, res);
88608
+ if (!authUser)
88609
+ return;
88610
+ if (!this.licenseService) {
88611
+ this.json(res, 503, { error: "License service not available" });
88612
+ return;
88613
+ }
88614
+ const body = await this.readBody(req);
88615
+ const licenseKey = body["licenseKey"];
88616
+ if (!licenseKey) {
88617
+ this.json(res, 400, { error: "licenseKey is required" });
88618
+ return;
88619
+ }
88620
+ const result = await this.licenseService.activateLicense(licenseKey);
88621
+ if (result.success && this.billingService)
88622
+ this.billingService.setOrgPlan("default", this.licenseService.getPlan());
88623
+ this.json(res, result.success ? 200 : 400, result);
88624
+ return;
88625
+ }
88626
+ if (path === "/api/license/trial" && req.method === "POST") {
88627
+ const authUser = await this.requireAuth(req, res);
88628
+ if (!authUser)
88629
+ return;
88630
+ if (!this.licenseService) {
88631
+ this.json(res, 503, { error: "License service not available" });
88632
+ return;
88633
+ }
88634
+ const result = await this.licenseService.activateTrial();
88635
+ if (result.success && this.billingService)
88636
+ this.billingService.setOrgPlan("default", this.licenseService.getPlan());
88637
+ this.json(res, result.success ? 200 : 400, result);
88638
+ return;
88639
+ }
88640
+ if (path === "/api/license/import" && req.method === "POST") {
88641
+ const authUser = await this.requireAuth(req, res);
88642
+ if (!authUser)
88643
+ return;
88644
+ if (!this.licenseService) {
88645
+ this.json(res, 503, { error: "License service not available" });
88646
+ return;
88647
+ }
88648
+ const body = await this.readBody(req);
88649
+ const fileContent = body["fileContent"];
88650
+ if (!fileContent) {
88651
+ this.json(res, 400, { error: "fileContent is required" });
88652
+ return;
88653
+ }
88654
+ const result = this.licenseService.importOfflineLicense(fileContent);
88655
+ if (result.success && this.billingService)
88656
+ this.billingService.setOrgPlan("default", this.licenseService.getPlan());
88657
+ this.json(res, result.success ? 200 : 400, result);
88658
+ return;
88659
+ }
88660
+ if (path === "/api/license/deactivate" && req.method === "POST") {
88661
+ const authUser = await this.requireAuth(req, res);
88662
+ if (!authUser)
88663
+ return;
88664
+ if (!this.licenseService) {
88665
+ this.json(res, 503, { error: "License service not available" });
88666
+ return;
88667
+ }
88668
+ await this.licenseService.deactivate();
88669
+ if (this.billingService)
88670
+ this.billingService.setOrgPlan("default", this.licenseService.getPlan());
88671
+ this.json(res, 200, { ok: true });
88672
+ return;
88673
+ }
88674
+ if (path === "/api/settings/telemetry" && req.method === "POST") {
88675
+ const body = await this.readBody(req);
88676
+ const enabled = body["enabled"];
88677
+ if (this.telemetryService && typeof enabled === "boolean") {
88678
+ this.telemetryService.setEnabled(enabled);
88679
+ }
88680
+ this.json(res, 200, { ok: true });
88681
+ return;
88682
+ }
88683
+ if (path === "/api/settings/telemetry" && req.method === "GET") {
88684
+ this.json(res, 200, { enabled: this.telemetryService?.isEnabled() ?? false });
88685
+ return;
88686
+ }
88247
88687
  if (path === "/api/hub/publish" && req.method === "POST") {
88248
88688
  const authUser = await this.requireAuth(req, res);
88249
88689
  if (!authUser)
@@ -88291,8 +88731,16 @@ EXPLANATION_END`;
88291
88731
  else
88292
88732
  proxyHeaders["Content-Type"] = req.headers["content-type"];
88293
88733
  const authHeader = req.headers["authorization"];
88294
- if (authHeader)
88734
+ if (authHeader) {
88295
88735
  proxyHeaders["Authorization"] = authHeader;
88736
+ } else {
88737
+ const storedToken = this.readHubToken();
88738
+ if (storedToken)
88739
+ proxyHeaders["Authorization"] = `Bearer ${storedToken}`;
88740
+ }
88741
+ if (req.headers["accept-language"]) {
88742
+ proxyHeaders["Accept-Language"] = req.headers["accept-language"];
88743
+ }
88296
88744
  try {
88297
88745
  let body;
88298
88746
  if (req.method === "POST" || req.method === "PUT" || req.method === "PATCH") {
@@ -88377,6 +88825,10 @@ EXPLANATION_END`;
88377
88825
  }
88378
88826
  this.remoteAgent.start().catch(() => {
88379
88827
  });
88828
+ try {
88829
+ saveConfig({ remote: { enabled: true } }, this.markusConfigPath);
88830
+ } catch {
88831
+ }
88380
88832
  this.json(res, 200, { ok: true, status: this.remoteAgent.getStatus() });
88381
88833
  return;
88382
88834
  }
@@ -88384,6 +88836,10 @@ EXPLANATION_END`;
88384
88836
  if (this.remoteAgent) {
88385
88837
  await this.remoteAgent.stop();
88386
88838
  }
88839
+ try {
88840
+ saveConfig({ remote: { enabled: false } }, this.markusConfigPath);
88841
+ } catch {
88842
+ }
88387
88843
  this.json(res, 200, { ok: true });
88388
88844
  return;
88389
88845
  }
@@ -88661,7 +89117,7 @@ EXPLANATION_END`;
88661
89117
  const { fileURLToPath: fileURLToPath8 } = await import("node:url");
88662
89118
  const { dirname: dn, resolve: rslv, join: jn } = await import("node:path");
88663
89119
  const { execSync: execSync7 } = await import("node:child_process");
88664
- const { existsSync: ex, readFileSync: readFileSync29, statSync: statSync8 } = await import("node:fs");
89120
+ const { existsSync: ex, readFileSync: readFileSync31, statSync: statSync8 } = await import("node:fs");
88665
89121
  const thisDir = dn(fileURLToPath8(import.meta.url));
88666
89122
  const zipCandidates = [
88667
89123
  jn(rslv(thisDir, "..", "..", "chrome-extension"), "dist", "markus-browser-extension.zip"),
@@ -88690,7 +89146,7 @@ EXPLANATION_END`;
88690
89146
  this.json(res, 404, { error: "Extension zip not found." });
88691
89147
  return;
88692
89148
  }
88693
- const data = readFileSync29(zipPath);
89149
+ const data = readFileSync31(zipPath);
88694
89150
  res.writeHead(200, {
88695
89151
  "Content-Type": "application/zip",
88696
89152
  "Content-Disposition": 'attachment; filename="markus-browser-extension.zip"',
@@ -88708,11 +89164,11 @@ EXPLANATION_END`;
88708
89164
  return;
88709
89165
  try {
88710
89166
  const { exec: execCb2 } = await import("node:child_process");
88711
- const platform4 = process.platform;
88712
- if (platform4 === "darwin") {
89167
+ const platform5 = process.platform;
89168
+ if (platform5 === "darwin") {
88713
89169
  execCb2('open -a "Google Chrome" "chrome://extensions"', () => {
88714
89170
  });
88715
- } else if (platform4 === "win32") {
89171
+ } else if (platform5 === "win32") {
88716
89172
  execCb2('start "" "chrome://extensions"', () => {
88717
89173
  });
88718
89174
  } else {
@@ -89791,11 +90247,11 @@ data: ${JSON.stringify({ error: msg })}
89791
90247
  const { configPath, preview } = body;
89792
90248
  const { existsSync: fsExists, readFileSync: fsRead } = await import("node:fs");
89793
90249
  const { join: pathJoin } = await import("node:path");
89794
- const { homedir: homedir23 } = await import("node:os");
90250
+ const { homedir: homedir25 } = await import("node:os");
89795
90251
  const possiblePaths = [
89796
90252
  configPath,
89797
- pathJoin(homedir23(), ".openclaw", "openclaw.json"),
89798
- pathJoin(homedir23(), ".openclaw", "openclaw.json5")
90253
+ pathJoin(homedir25(), ".openclaw", "openclaw.json"),
90254
+ pathJoin(homedir25(), ".openclaw", "openclaw.json5")
89799
90255
  ].filter(Boolean);
89800
90256
  let found = "";
89801
90257
  let rawContent = "";
@@ -90014,10 +90470,10 @@ data: ${JSON.stringify({ error: msg })}
90014
90470
  this.json(res, 400, { error: "Invalid or non-existent path" });
90015
90471
  return;
90016
90472
  }
90017
- const platform4 = process.platform;
90018
- if (platform4 === "darwin")
90473
+ const platform5 = process.platform;
90474
+ if (platform5 === "darwin")
90019
90475
  execSync3(`open ${JSON.stringify(dirPath)}`);
90020
- else if (platform4 === "win32")
90476
+ else if (platform5 === "win32")
90021
90477
  execSync3(`explorer ${JSON.stringify(dirPath)}`);
90022
90478
  else
90023
90479
  execSync3(`xdg-open ${JSON.stringify(dirPath)}`);
@@ -90169,9 +90625,9 @@ data: ${JSON.stringify({ error: msg })}
90169
90625
  }
90170
90626
  try {
90171
90627
  const { resolve: resolve21, extname: extname2 } = await import("node:path");
90172
- const { existsSync: existsSync40, statSync: statSync8 } = await import("node:fs");
90173
- const { homedir: homedir23 } = await import("node:os");
90174
- const home = homedir23();
90628
+ const { existsSync: existsSync42, statSync: statSync8 } = await import("node:fs");
90629
+ const { homedir: homedir25 } = await import("node:os");
90630
+ const home = homedir25();
90175
90631
  const results = {};
90176
90632
  const mdExts = [".md", ".markdown"];
90177
90633
  const htmlExts = [".html", ".htm"];
@@ -90181,7 +90637,7 @@ data: ${JSON.stringify({ error: msg })}
90181
90637
  try {
90182
90638
  const expanded = p.startsWith("~/") ? resolve21(home, p.slice(2)) : p === "~" ? home : p;
90183
90639
  const resolved = resolve21(expanded);
90184
- if (!existsSync40(resolved)) {
90640
+ if (!existsSync42(resolved)) {
90185
90641
  results[p] = { exists: false, isFile: false, type: "unknown" };
90186
90642
  continue;
90187
90643
  }
@@ -90218,21 +90674,21 @@ data: ${JSON.stringify({ error: msg })}
90218
90674
  }
90219
90675
  try {
90220
90676
  const { resolve: resolve21, extname: extname2 } = await import("node:path");
90221
- const { readFileSync: readFileSync29, existsSync: existsSync40, statSync: statSync8 } = await import("node:fs");
90222
- const { homedir: homedir23 } = await import("node:os");
90223
- const home = homedir23();
90677
+ const { readFileSync: readFileSync31, existsSync: existsSync42, statSync: statSync8 } = await import("node:fs");
90678
+ const { homedir: homedir25 } = await import("node:os");
90679
+ const home = homedir25();
90224
90680
  const expanded = filePath.startsWith("~/") ? resolve21(home, filePath.slice(2)) : filePath === "~" ? home : filePath;
90225
90681
  const resolved = resolve21(expanded);
90226
- if (!existsSync40(resolved)) {
90682
+ if (!existsSync42(resolved)) {
90227
90683
  this.json(res, 404, { error: "File not found" });
90228
90684
  return;
90229
90685
  }
90230
90686
  const stat = statSync8(resolved);
90231
90687
  if (stat.isDirectory()) {
90232
90688
  const { readdirSync: readdirSync14 } = await import("node:fs");
90233
- const { join: join33, extname: extDir } = await import("node:path");
90689
+ const { join: join35, extname: extDir } = await import("node:path");
90234
90690
  const entries2 = readdirSync14(resolved, { withFileTypes: true }).filter((e) => !e.name.startsWith(".")).map((e) => {
90235
- const full = join33(resolved, e.name);
90691
+ const full = join35(resolved, e.name);
90236
90692
  const isDir = e.isDirectory();
90237
90693
  let size;
90238
90694
  try {
@@ -90261,7 +90717,7 @@ data: ${JSON.stringify({ error: msg })}
90261
90717
  const ext = extname2(resolved).toLowerCase();
90262
90718
  const imageExts = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"];
90263
90719
  if (imageExts.includes(ext)) {
90264
- const data = readFileSync29(resolved);
90720
+ const data = readFileSync31(resolved);
90265
90721
  const mimeMap = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".svg": "image/svg+xml" };
90266
90722
  this.json(res, 200, {
90267
90723
  type: "image",
@@ -90270,7 +90726,7 @@ data: ${JSON.stringify({ error: msg })}
90270
90726
  content: data.toString("base64")
90271
90727
  });
90272
90728
  } else {
90273
- const content = readFileSync29(resolved, "utf-8");
90729
+ const content = readFileSync31(resolved, "utf-8");
90274
90730
  const mdExts = [".md", ".markdown"];
90275
90731
  const htmlExts = [".html", ".htm"];
90276
90732
  const jsonExts = [".json"];
@@ -90303,12 +90759,12 @@ data: ${JSON.stringify({ error: msg })}
90303
90759
  }
90304
90760
  try {
90305
90761
  const { resolve: resolve21, extname: extname2 } = await import("node:path");
90306
- const { readFileSync: readFileSync29, existsSync: existsSync40, statSync: statSync8 } = await import("node:fs");
90307
- const { homedir: homedir23 } = await import("node:os");
90308
- const home = homedir23();
90762
+ const { readFileSync: readFileSync31, existsSync: existsSync42, statSync: statSync8 } = await import("node:fs");
90763
+ const { homedir: homedir25 } = await import("node:os");
90764
+ const home = homedir25();
90309
90765
  const expanded = filePath.startsWith("~/") ? resolve21(home, filePath.slice(2)) : filePath === "~" ? home : filePath;
90310
90766
  const resolved = resolve21(expanded);
90311
- if (!existsSync40(resolved) || !statSync8(resolved).isFile()) {
90767
+ if (!existsSync42(resolved) || !statSync8(resolved).isFile()) {
90312
90768
  this.json(res, 404, { error: "Image not found" });
90313
90769
  return;
90314
90770
  }
@@ -90333,7 +90789,7 @@ data: ${JSON.stringify({ error: msg })}
90333
90789
  this.json(res, 400, { error: "Not an image file" });
90334
90790
  return;
90335
90791
  }
90336
- const data = readFileSync29(resolved);
90792
+ const data = readFileSync31(resolved);
90337
90793
  res.writeHead(200, {
90338
90794
  "Content-Type": mime,
90339
90795
  "Content-Length": data.length,
@@ -90353,26 +90809,26 @@ data: ${JSON.stringify({ error: msg })}
90353
90809
  return;
90354
90810
  }
90355
90811
  try {
90356
- const { resolve: resolve21, dirname: dirname13 } = await import("node:path");
90357
- const { existsSync: existsSync40, statSync: statSync8 } = await import("node:fs");
90812
+ const { resolve: resolve21, dirname: dirname15 } = await import("node:path");
90813
+ const { existsSync: existsSync42, statSync: statSync8 } = await import("node:fs");
90358
90814
  const { exec: exec2 } = await import("node:child_process");
90359
- const { homedir: homedir23 } = await import("node:os");
90360
- const home = homedir23();
90815
+ const { homedir: homedir25 } = await import("node:os");
90816
+ const home = homedir25();
90361
90817
  const expanded = filePath.startsWith("~/") ? resolve21(home, filePath.slice(2)) : filePath === "~" ? home : filePath;
90362
90818
  const resolved = resolve21(expanded);
90363
- if (!existsSync40(resolved)) {
90819
+ if (!existsSync42(resolved)) {
90364
90820
  this.json(res, 404, { error: "Path not found" });
90365
90821
  return;
90366
90822
  }
90367
90823
  const isDir = statSync8(resolved).isDirectory();
90368
- const platform4 = process.platform;
90824
+ const platform5 = process.platform;
90369
90825
  let cmd;
90370
- if (platform4 === "darwin") {
90826
+ if (platform5 === "darwin") {
90371
90827
  cmd = isDir ? `open "${resolved}"` : `open -R "${resolved}"`;
90372
- } else if (platform4 === "win32") {
90828
+ } else if (platform5 === "win32") {
90373
90829
  cmd = isDir ? `explorer "${resolved}"` : `explorer /select,"${resolved}"`;
90374
90830
  } else {
90375
- cmd = `xdg-open "${isDir ? resolved : dirname13(resolved)}"`;
90831
+ cmd = `xdg-open "${isDir ? resolved : dirname15(resolved)}"`;
90376
90832
  }
90377
90833
  exec2(cmd, (err) => {
90378
90834
  if (err) {
@@ -91069,6 +91525,7 @@ data: ${JSON.stringify({ error: msg })}
91069
91525
  return [
91070
91526
  // ── Auth ─────────────────────────────────────────────────────────────
91071
91527
  exact("/api/auth/login", "POST"),
91528
+ exact("/api/auth/hub-login", "POST"),
91072
91529
  exact("/api/auth/logout", "POST"),
91073
91530
  exact("/api/auth/me", "GET"),
91074
91531
  exact("/api/auth/change-password", "POST"),
@@ -91228,6 +91685,13 @@ data: ${JSON.stringify({ error: msg })}
91228
91685
  exact("/api/models/validate-key", "POST"),
91229
91686
  regex(/^\/api\/models\/live\/[^/]+$/, "GET"),
91230
91687
  // ── Settings ─────────────────────────────────────────────────────────
91688
+ exact("/api/license", "GET"),
91689
+ exact("/api/license/refresh", "POST"),
91690
+ exact("/api/license/activate", "POST"),
91691
+ exact("/api/license/trial", "POST"),
91692
+ exact("/api/license/import", "POST"),
91693
+ exact("/api/license/deactivate", "POST"),
91694
+ exact("/api/settings/telemetry", "GET", "POST"),
91231
91695
  exact("/api/settings/hub", "GET"),
91232
91696
  exact("/api/settings/hub-token", "POST"),
91233
91697
  exact("/api/settings/llm", "GET", "POST"),
@@ -92376,17 +92840,10 @@ var init_billing_service = __esm({
92376
92840
  DEFAULT_PLANS = {
92377
92841
  free: {
92378
92842
  maxAgents: -1,
92379
- maxTokensPerMonth: 1e5,
92380
- maxToolCallsPerDay: 100,
92381
- maxMessagesPerDay: 50,
92382
- maxStorageBytes: 50 * 1024 * 1024
92383
- },
92384
- pro: {
92385
- maxAgents: 20,
92386
- maxTokensPerMonth: 5e6,
92387
- maxToolCallsPerDay: 5e3,
92388
- maxMessagesPerDay: 2e3,
92389
- maxStorageBytes: 5 * 1024 * 1024 * 1024
92843
+ maxTokensPerMonth: -1,
92844
+ maxToolCallsPerDay: 500,
92845
+ maxMessagesPerDay: -1,
92846
+ maxStorageBytes: -1
92390
92847
  },
92391
92848
  enterprise: {
92392
92849
  maxAgents: -1,
@@ -92402,6 +92859,10 @@ var init_billing_service = __esm({
92402
92859
  apiKeys = /* @__PURE__ */ new Map();
92403
92860
  apiKeysByKey = /* @__PURE__ */ new Map();
92404
92861
  orgPlans = /* @__PURE__ */ new Map();
92862
+ toolCallsTodayProvider;
92863
+ setToolCallsTodayProvider(fn) {
92864
+ this.toolCallsTodayProvider = fn;
92865
+ }
92405
92866
  setOrgPlan(orgId2, tier) {
92406
92867
  const plan = {
92407
92868
  orgId: orgId2,
@@ -92477,8 +92938,7 @@ var init_billing_service = __esm({
92477
92938
  }
92478
92939
  }
92479
92940
  if (type === "tool_call") {
92480
- const todayRecords = this.records.filter((r) => r.orgId === orgId2 && r.type === "tool_call" && r.timestamp.startsWith(today));
92481
- const todayCount = todayRecords.reduce((s2, r) => s2 + r.amount, 0);
92941
+ const todayCount = this.toolCallsTodayProvider ? this.toolCallsTodayProvider() : this.records.filter((r) => r.orgId === orgId2 && r.type === "tool_call" && r.timestamp.startsWith(today)).reduce((s2, r) => s2 + r.amount, 0);
92482
92942
  if (plan.limits.maxToolCallsPerDay > 0 && todayCount + additionalAmount > plan.limits.maxToolCallsPerDay) {
92483
92943
  return {
92484
92944
  allowed: false,
@@ -92606,13 +93066,523 @@ var init_billing_service = __esm({
92606
93066
  }
92607
93067
  });
92608
93068
 
93069
+ // ../org-manager/dist/license-service.js
93070
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync18, existsSync as existsSync30, mkdirSync as mkdirSync20 } from "node:fs";
93071
+ import { join as join25, dirname as dirname9 } from "node:path";
93072
+ import { homedir as homedir16 } from "node:os";
93073
+ import { randomUUID, createVerify } from "node:crypto";
93074
+ async function hubFetch(url, init, maxRedirects = 3) {
93075
+ let currentUrl = url;
93076
+ for (let i = 0; i <= maxRedirects; i++) {
93077
+ const res = await fetch(currentUrl, { ...init, redirect: "manual" });
93078
+ if (res.status >= 300 && res.status < 400) {
93079
+ const location = res.headers.get("location");
93080
+ if (!location)
93081
+ break;
93082
+ currentUrl = new URL(location, currentUrl).href;
93083
+ continue;
93084
+ }
93085
+ return res;
93086
+ }
93087
+ return fetch(currentUrl, init);
93088
+ }
93089
+ var log64, LICENSE_FILE, HEARTBEAT_INTERVAL_MS, HUB_LICENSE_PUBLIC_KEY, LicenseService;
93090
+ var init_license_service = __esm({
93091
+ "../org-manager/dist/license-service.js"() {
93092
+ "use strict";
93093
+ init_dist();
93094
+ log64 = createLogger("license");
93095
+ LICENSE_FILE = join25(homedir16(), ".markus", "license.json");
93096
+ HEARTBEAT_INTERVAL_MS = 4 * 60 * 60 * 1e3;
93097
+ HUB_LICENSE_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
93098
+ MCowBQYDK2VwAyEAPlaceholderPublicKeyForOfflineLicenseVerification00=
93099
+ -----END PUBLIC KEY-----`;
93100
+ LicenseService = class {
93101
+ license;
93102
+ hubUrl;
93103
+ heartbeatTimer = null;
93104
+ constructor(hubUrl = "https://markus.global") {
93105
+ this.hubUrl = hubUrl;
93106
+ this.license = this.loadLicense();
93107
+ this.startHeartbeat();
93108
+ }
93109
+ loadLicense() {
93110
+ try {
93111
+ if (existsSync30(LICENSE_FILE)) {
93112
+ const data = JSON.parse(readFileSync23(LICENSE_FILE, "utf-8"));
93113
+ if (data && data.plan && data.instanceId) {
93114
+ return data;
93115
+ }
93116
+ }
93117
+ } catch (e) {
93118
+ log64.warn("Failed to load license file, using defaults");
93119
+ }
93120
+ const defaultLicense = {
93121
+ plan: "free",
93122
+ features: [],
93123
+ limits: { ...PLAN_LIMITS.free },
93124
+ instanceId: randomUUID()
93125
+ };
93126
+ this.saveLicense(defaultLicense);
93127
+ return defaultLicense;
93128
+ }
93129
+ saveLicense(license) {
93130
+ try {
93131
+ mkdirSync20(dirname9(LICENSE_FILE), { recursive: true });
93132
+ writeFileSync18(LICENSE_FILE, JSON.stringify(license, null, 2), "utf-8");
93133
+ } catch (e) {
93134
+ log64.warn("Failed to save license file");
93135
+ }
93136
+ }
93137
+ startHeartbeat() {
93138
+ if (this.heartbeatTimer)
93139
+ clearInterval(this.heartbeatTimer);
93140
+ this.heartbeatTimer = setInterval(() => {
93141
+ void this.sendHeartbeat();
93142
+ }, HEARTBEAT_INTERVAL_MS);
93143
+ setTimeout(() => void this.sendHeartbeat(), 3e4);
93144
+ }
93145
+ async sendHeartbeat() {
93146
+ if (!this.license.licenseKey)
93147
+ return;
93148
+ const hubToken = this.readHubToken();
93149
+ if (!hubToken)
93150
+ return;
93151
+ try {
93152
+ const res = await hubFetch(`${this.hubUrl}/api/licenses/heartbeat`, {
93153
+ method: "POST",
93154
+ headers: {
93155
+ "Content-Type": "application/json",
93156
+ "Authorization": `Bearer ${hubToken}`
93157
+ },
93158
+ body: JSON.stringify({
93159
+ licenseKey: this.license.licenseKey,
93160
+ instanceId: this.license.instanceId
93161
+ })
93162
+ });
93163
+ if (res.ok) {
93164
+ const data = await res.json();
93165
+ if (data.valid) {
93166
+ this.license.lastValidated = (/* @__PURE__ */ new Date()).toISOString();
93167
+ this.license.plan = data.plan;
93168
+ this.license.validUntil = data.validUntil;
93169
+ this.license.limits = { ...PLAN_LIMITS[data.plan] };
93170
+ this.license.features = data.plan === "enterprise" ? [...ENTERPRISE_FEATURES] : [];
93171
+ if (data.orgId)
93172
+ this.license.orgId = data.orgId;
93173
+ if (data.orgName)
93174
+ this.license.orgName = data.orgName;
93175
+ if (data.maxSeats !== null && data.maxSeats !== void 0)
93176
+ this.license.maxSeats = data.maxSeats;
93177
+ if (data.usedSeats !== null && data.usedSeats !== void 0)
93178
+ this.license.usedSeats = data.usedSeats;
93179
+ this.saveLicense(this.license);
93180
+ } else {
93181
+ log64.warn("License heartbeat returned invalid \u2014 reverting to free");
93182
+ this.revertToFree();
93183
+ }
93184
+ } else if (res.status === 403 || res.status === 404) {
93185
+ log64.warn("License heartbeat rejected \u2014 reverting to free");
93186
+ this.revertToFree();
93187
+ }
93188
+ } catch {
93189
+ log64.debug("License heartbeat failed (network issue) \u2014 using cached state");
93190
+ }
93191
+ }
93192
+ revertToFree() {
93193
+ this.license.plan = "free";
93194
+ this.license.licenseKey = void 0;
93195
+ this.license.validUntil = void 0;
93196
+ this.license.isTrial = void 0;
93197
+ this.license.isOffline = void 0;
93198
+ this.license.features = [];
93199
+ this.license.limits = { ...PLAN_LIMITS.free };
93200
+ this.license.orgId = void 0;
93201
+ this.license.orgName = void 0;
93202
+ this.license.maxSeats = void 0;
93203
+ this.license.usedSeats = void 0;
93204
+ this.saveLicense(this.license);
93205
+ }
93206
+ readHubToken() {
93207
+ try {
93208
+ const tokenPath = join25(homedir16(), ".markus", "hub-token");
93209
+ return existsSync30(tokenPath) ? readFileSync23(tokenPath, "utf-8").trim() : void 0;
93210
+ } catch {
93211
+ return void 0;
93212
+ }
93213
+ }
93214
+ // ─── Public API ────────────────────────────────────────────────────────
93215
+ getPlan() {
93216
+ if (this.license.validUntil && new Date(this.license.validUntil) < /* @__PURE__ */ new Date()) {
93217
+ if (this.license.plan !== "free") {
93218
+ log64.info("License expired \u2014 reverting to free");
93219
+ this.revertToFree();
93220
+ }
93221
+ }
93222
+ return this.license.plan;
93223
+ }
93224
+ getLimits() {
93225
+ this.getPlan();
93226
+ return { ...this.license.limits };
93227
+ }
93228
+ getFeatures() {
93229
+ this.getPlan();
93230
+ return [...this.license.features];
93231
+ }
93232
+ canUse(feature) {
93233
+ this.getPlan();
93234
+ if (this.license.plan === "enterprise")
93235
+ return true;
93236
+ return this.license.features.includes(feature);
93237
+ }
93238
+ getInfo() {
93239
+ this.getPlan();
93240
+ return { ...this.license };
93241
+ }
93242
+ getInstanceId() {
93243
+ return this.license.instanceId;
93244
+ }
93245
+ async activateLicense(licenseKey) {
93246
+ const hubToken = this.readHubToken();
93247
+ if (!hubToken) {
93248
+ return { success: false, error: "Not authenticated with Markus Hub" };
93249
+ }
93250
+ try {
93251
+ const res = await hubFetch(`${this.hubUrl}/api/licenses/activate`, {
93252
+ method: "POST",
93253
+ headers: {
93254
+ "Content-Type": "application/json",
93255
+ "Authorization": `Bearer ${hubToken}`
93256
+ },
93257
+ body: JSON.stringify({
93258
+ licenseKey,
93259
+ instanceId: this.license.instanceId
93260
+ })
93261
+ });
93262
+ const data = await res.json();
93263
+ if (res.ok && data.success) {
93264
+ this.license.licenseKey = licenseKey;
93265
+ this.license.plan = data.plan ?? "enterprise";
93266
+ this.license.validUntil = data.validUntil;
93267
+ this.license.isTrial = data.isTrial;
93268
+ this.license.isOffline = false;
93269
+ this.license.features = data.features ?? [...ENTERPRISE_FEATURES];
93270
+ this.license.limits = { ...PLAN_LIMITS[this.license.plan] };
93271
+ this.license.lastValidated = (/* @__PURE__ */ new Date()).toISOString();
93272
+ this.license.orgId = data.orgId;
93273
+ this.license.orgName = data.orgName;
93274
+ this.license.maxSeats = data.maxSeats;
93275
+ this.license.usedSeats = data.usedSeats;
93276
+ this.saveLicense(this.license);
93277
+ log64.info(`License activated: ${this.license.plan} (valid until ${this.license.validUntil})`);
93278
+ return { success: true };
93279
+ }
93280
+ return { success: false, error: data.error ?? "Activation failed" };
93281
+ } catch {
93282
+ return { success: false, error: "Could not connect to Markus Hub" };
93283
+ }
93284
+ }
93285
+ async activateTrial() {
93286
+ const hubToken = this.readHubToken();
93287
+ if (!hubToken) {
93288
+ return { success: false, error: "Not authenticated with Markus Hub" };
93289
+ }
93290
+ try {
93291
+ const res = await hubFetch(`${this.hubUrl}/api/licenses/trial`, {
93292
+ method: "POST",
93293
+ headers: {
93294
+ "Content-Type": "application/json",
93295
+ "Authorization": `Bearer ${hubToken}`
93296
+ },
93297
+ body: JSON.stringify({
93298
+ instanceId: this.license.instanceId
93299
+ })
93300
+ });
93301
+ const data = await res.json();
93302
+ if (res.ok && data.success && data.licenseKey) {
93303
+ this.license.licenseKey = data.licenseKey;
93304
+ this.license.plan = data.plan ?? "enterprise";
93305
+ this.license.validUntil = data.validUntil;
93306
+ this.license.isTrial = true;
93307
+ this.license.isOffline = false;
93308
+ this.license.features = [...ENTERPRISE_FEATURES];
93309
+ this.license.limits = { ...PLAN_LIMITS.enterprise };
93310
+ this.license.lastValidated = (/* @__PURE__ */ new Date()).toISOString();
93311
+ this.license.orgId = data.orgId;
93312
+ this.license.orgName = data.orgName;
93313
+ this.license.maxSeats = data.maxSeats;
93314
+ this.saveLicense(this.license);
93315
+ log64.info(`Trial activated (valid until ${this.license.validUntil})`);
93316
+ return { success: true };
93317
+ }
93318
+ return { success: false, error: data.error ?? "Trial activation failed" };
93319
+ } catch {
93320
+ return { success: false, error: "Could not connect to Markus Hub" };
93321
+ }
93322
+ }
93323
+ importOfflineLicense(fileContent) {
93324
+ try {
93325
+ const payload = JSON.parse(fileContent);
93326
+ if (payload.version !== 1 || payload.plan !== "enterprise") {
93327
+ return { success: false, error: "Invalid license file format" };
93328
+ }
93329
+ if (new Date(payload.validUntil) < /* @__PURE__ */ new Date()) {
93330
+ return { success: false, error: "License has expired" };
93331
+ }
93332
+ if (payload.signature) {
93333
+ try {
93334
+ const verifier = createVerify("Ed25519");
93335
+ const signData = JSON.stringify({
93336
+ version: payload.version,
93337
+ licenseId: payload.licenseId,
93338
+ plan: payload.plan,
93339
+ issuedTo: payload.issuedTo,
93340
+ validFrom: payload.validFrom,
93341
+ validUntil: payload.validUntil,
93342
+ maxInstances: payload.maxInstances,
93343
+ features: payload.features
93344
+ });
93345
+ verifier.update(signData);
93346
+ const valid = verifier.verify(HUB_LICENSE_PUBLIC_KEY, payload.signature, "base64");
93347
+ if (!valid) {
93348
+ log64.warn("Offline license signature verification failed \u2014 accepting in dev mode");
93349
+ }
93350
+ } catch {
93351
+ log64.warn("Offline license signature verification skipped (key format)");
93352
+ }
93353
+ }
93354
+ this.license.licenseKey = payload.licenseId;
93355
+ this.license.plan = "enterprise";
93356
+ this.license.validUntil = payload.validUntil;
93357
+ this.license.isTrial = false;
93358
+ this.license.isOffline = true;
93359
+ this.license.features = payload.features ?? [...ENTERPRISE_FEATURES];
93360
+ this.license.limits = { ...PLAN_LIMITS.enterprise };
93361
+ this.license.lastValidated = (/* @__PURE__ */ new Date()).toISOString();
93362
+ this.saveLicense(this.license);
93363
+ log64.info(`Offline license imported: ${payload.licenseId} (valid until ${payload.validUntil})`);
93364
+ return { success: true };
93365
+ } catch {
93366
+ return { success: false, error: "Could not parse license file" };
93367
+ }
93368
+ }
93369
+ async deactivate() {
93370
+ if (this.license.licenseKey && !this.license.isOffline) {
93371
+ const hubToken = this.readHubToken();
93372
+ if (hubToken) {
93373
+ try {
93374
+ await hubFetch(`${this.hubUrl}/api/licenses/deactivate`, {
93375
+ method: "POST",
93376
+ headers: {
93377
+ "Content-Type": "application/json",
93378
+ "Authorization": `Bearer ${hubToken}`
93379
+ },
93380
+ body: JSON.stringify({
93381
+ licenseKey: this.license.licenseKey,
93382
+ instanceId: this.license.instanceId
93383
+ })
93384
+ });
93385
+ } catch {
93386
+ }
93387
+ }
93388
+ }
93389
+ this.revertToFree();
93390
+ log64.info("License deactivated");
93391
+ }
93392
+ async revalidate() {
93393
+ if (!this.license.licenseKey) {
93394
+ const saved = this.loadLicense();
93395
+ if (saved.licenseKey) {
93396
+ this.license = saved;
93397
+ }
93398
+ }
93399
+ await this.syncFromHub();
93400
+ if (this.license.licenseKey) {
93401
+ await this.sendHeartbeat();
93402
+ }
93403
+ this.getPlan();
93404
+ return { ...this.license };
93405
+ }
93406
+ async syncFromHub() {
93407
+ const hubToken = this.readHubToken();
93408
+ if (!hubToken)
93409
+ return;
93410
+ try {
93411
+ const res = await hubFetch(`${this.hubUrl}/api/licenses/mine`, {
93412
+ headers: { "Authorization": `Bearer ${hubToken}` }
93413
+ });
93414
+ if (!res.ok)
93415
+ return;
93416
+ const data = await res.json();
93417
+ if (!data.license)
93418
+ return;
93419
+ const currentKey = this.license.licenseKey;
93420
+ if (currentKey === data.license.licenseKey) {
93421
+ if (data.license.usedSeats !== null && data.license.usedSeats !== void 0 && data.license.usedSeats !== this.license.usedSeats) {
93422
+ this.license.usedSeats = data.license.usedSeats;
93423
+ this.saveLicense(this.license);
93424
+ }
93425
+ return;
93426
+ }
93427
+ const currentIsTrial = this.license.isTrial;
93428
+ const newIsBetter = !currentKey || currentIsTrial && !data.license.isTrial || !currentIsTrial && !data.license.isTrial && new Date(data.license.validUntil) > new Date(this.license.validUntil ?? "");
93429
+ if (!newIsBetter)
93430
+ return;
93431
+ log64.info(`Found better license on Hub: ${data.license.licenseKey} (current: ${currentKey ?? "none"})`);
93432
+ const result = await this.activateLicense(data.license.licenseKey);
93433
+ if (result.success) {
93434
+ if (data.license.orgId)
93435
+ this.license.orgId = data.license.orgId;
93436
+ if (data.license.orgName)
93437
+ this.license.orgName = data.license.orgName;
93438
+ if (data.license.maxSeats !== null && data.license.maxSeats !== void 0)
93439
+ this.license.maxSeats = data.license.maxSeats;
93440
+ this.saveLicense(this.license);
93441
+ log64.info(`Upgraded license from Hub: ${data.license.licenseKey}`);
93442
+ }
93443
+ } catch {
93444
+ log64.debug("Failed to sync license from Hub");
93445
+ }
93446
+ }
93447
+ setHubUrl(url) {
93448
+ this.hubUrl = url;
93449
+ }
93450
+ destroy() {
93451
+ if (this.heartbeatTimer) {
93452
+ clearInterval(this.heartbeatTimer);
93453
+ this.heartbeatTimer = null;
93454
+ }
93455
+ }
93456
+ };
93457
+ }
93458
+ });
93459
+
93460
+ // ../org-manager/dist/telemetry-service.js
93461
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync19, existsSync as existsSync31, mkdirSync as mkdirSync21 } from "node:fs";
93462
+ import { join as join26, dirname as dirname10 } from "node:path";
93463
+ import { homedir as homedir17, platform as platform3, arch as arch2 } from "node:os";
93464
+ async function hubFetch2(url, init) {
93465
+ let currentUrl = url;
93466
+ for (let i = 0; i < 3; i++) {
93467
+ const res = await fetch(currentUrl, { ...init, redirect: "manual" });
93468
+ if (res.status >= 300 && res.status < 400) {
93469
+ const location = res.headers.get("location");
93470
+ if (!location)
93471
+ return res;
93472
+ currentUrl = new URL(location, currentUrl).href;
93473
+ continue;
93474
+ }
93475
+ return res;
93476
+ }
93477
+ return fetch(currentUrl, init);
93478
+ }
93479
+ var log65, TELEMETRY_CONFIG_FILE, REPORT_INTERVAL_MS, TelemetryService;
93480
+ var init_telemetry_service = __esm({
93481
+ "../org-manager/dist/telemetry-service.js"() {
93482
+ "use strict";
93483
+ init_dist();
93484
+ log65 = createLogger("telemetry");
93485
+ TELEMETRY_CONFIG_FILE = join26(homedir17(), ".markus", "telemetry.json");
93486
+ REPORT_INTERVAL_MS = 6 * 60 * 60 * 1e3;
93487
+ TelemetryService = class {
93488
+ enabled;
93489
+ hubUrl;
93490
+ instanceId;
93491
+ timer = null;
93492
+ statsProvider = null;
93493
+ constructor(hubUrl, instanceId) {
93494
+ this.hubUrl = hubUrl;
93495
+ this.instanceId = instanceId;
93496
+ const config = this.loadConfig();
93497
+ this.enabled = config.enabled;
93498
+ }
93499
+ loadConfig() {
93500
+ try {
93501
+ if (existsSync31(TELEMETRY_CONFIG_FILE)) {
93502
+ return JSON.parse(readFileSync24(TELEMETRY_CONFIG_FILE, "utf-8"));
93503
+ }
93504
+ } catch {
93505
+ }
93506
+ return { enabled: true };
93507
+ }
93508
+ saveConfig(config) {
93509
+ try {
93510
+ mkdirSync21(dirname10(TELEMETRY_CONFIG_FILE), { recursive: true });
93511
+ writeFileSync19(TELEMETRY_CONFIG_FILE, JSON.stringify(config, null, 2), "utf-8");
93512
+ } catch {
93513
+ }
93514
+ }
93515
+ setEnabled(enabled) {
93516
+ this.enabled = enabled;
93517
+ const config = this.loadConfig();
93518
+ config.enabled = enabled;
93519
+ this.saveConfig(config);
93520
+ log65.info(`Telemetry ${enabled ? "enabled" : "disabled"}`);
93521
+ }
93522
+ isEnabled() {
93523
+ return this.enabled;
93524
+ }
93525
+ setStatsProvider(provider) {
93526
+ this.statsProvider = provider;
93527
+ }
93528
+ start() {
93529
+ if (this.timer)
93530
+ return;
93531
+ this.timer = setInterval(() => void this.report(), REPORT_INTERVAL_MS);
93532
+ setTimeout(() => void this.report(), 6e4);
93533
+ }
93534
+ async report() {
93535
+ if (!this.enabled || !this.statsProvider)
93536
+ return;
93537
+ try {
93538
+ const stats = this.statsProvider();
93539
+ const payload = {
93540
+ instanceId: this.instanceId,
93541
+ version: APP_VERSION,
93542
+ os: `${platform3()}/${arch2()}`,
93543
+ ...stats
93544
+ };
93545
+ const hubToken = this.readHubToken();
93546
+ const headers = { "Content-Type": "application/json" };
93547
+ if (hubToken)
93548
+ headers["Authorization"] = `Bearer ${hubToken}`;
93549
+ await hubFetch2(`${this.hubUrl}/api/telemetry`, {
93550
+ method: "POST",
93551
+ headers,
93552
+ body: JSON.stringify(payload)
93553
+ });
93554
+ const config = this.loadConfig();
93555
+ config.lastReportAt = (/* @__PURE__ */ new Date()).toISOString();
93556
+ this.saveConfig(config);
93557
+ } catch {
93558
+ log65.debug("Telemetry report failed (network)");
93559
+ }
93560
+ }
93561
+ readHubToken() {
93562
+ try {
93563
+ const tokenPath = join26(homedir17(), ".markus", "hub-token");
93564
+ return existsSync31(tokenPath) ? readFileSync24(tokenPath, "utf-8").trim() : void 0;
93565
+ } catch {
93566
+ return void 0;
93567
+ }
93568
+ }
93569
+ destroy() {
93570
+ if (this.timer) {
93571
+ clearInterval(this.timer);
93572
+ this.timer = null;
93573
+ }
93574
+ }
93575
+ };
93576
+ }
93577
+ });
93578
+
92609
93579
  // ../org-manager/dist/audit-service.js
92610
- var log64, entryCounter, AuditService;
93580
+ var log66, entryCounter, AuditService;
92611
93581
  var init_audit_service = __esm({
92612
93582
  "../org-manager/dist/audit-service.js"() {
92613
93583
  "use strict";
92614
93584
  init_dist();
92615
- log64 = createLogger("audit");
93585
+ log66 = createLogger("audit");
92616
93586
  entryCounter = 0;
92617
93587
  AuditService = class {
92618
93588
  entries = [];
@@ -92620,7 +93590,7 @@ var init_audit_service = __esm({
92620
93590
  db;
92621
93591
  setRepository(db) {
92622
93592
  this.db = db;
92623
- log64.info("Audit persistence enabled \u2014 events will be written to DB");
93593
+ log66.info("Audit persistence enabled \u2014 events will be written to DB");
92624
93594
  }
92625
93595
  record(entry) {
92626
93596
  const full = {
@@ -92646,7 +93616,7 @@ var init_audit_service = __esm({
92646
93616
  durationMs: full.durationMs,
92647
93617
  success: full.success,
92648
93618
  createdAt: new Date(full.timestamp)
92649
- }).catch((err) => log64.warn("Failed to persist audit entry", { id: full.id, error: String(err) }));
93619
+ }).catch((err) => log66.warn("Failed to persist audit entry", { id: full.id, error: String(err) }));
92650
93620
  }
92651
93621
  return full;
92652
93622
  }
@@ -92729,12 +93699,12 @@ var init_audit_service = __esm({
92729
93699
  });
92730
93700
 
92731
93701
  // ../org-manager/dist/project-service.js
92732
- var log65, ProjectService;
93702
+ var log67, ProjectService;
92733
93703
  var init_project_service = __esm({
92734
93704
  "../org-manager/dist/project-service.js"() {
92735
93705
  "use strict";
92736
93706
  init_dist();
92737
- log65 = createLogger("project-service");
93707
+ log67 = createLogger("project-service");
92738
93708
  ProjectService = class {
92739
93709
  projects = /* @__PURE__ */ new Map();
92740
93710
  projectRepo;
@@ -92764,9 +93734,9 @@ var init_project_service = __esm({
92764
93734
  };
92765
93735
  this.projects.set(project.id, project);
92766
93736
  }
92767
- log65.info(`Loaded ${this.projects.size} projects from DB`);
93737
+ log67.info(`Loaded ${this.projects.size} projects from DB`);
92768
93738
  } catch (err) {
92769
- log65.warn("Failed to load projects from DB", { error: String(err) });
93739
+ log67.warn("Failed to load projects from DB", { error: String(err) });
92770
93740
  }
92771
93741
  }
92772
93742
  }
@@ -92803,8 +93773,8 @@ var init_project_service = __esm({
92803
93773
  reportSchedule: project.reportSchedule,
92804
93774
  onboardingConfig: project.onboardingConfig,
92805
93775
  createdBy: opts.createdBy
92806
- }).catch((err) => log65.warn("Failed to persist project", { error: String(err) }));
92807
- log65.info("Project created", { id: project.id, name: project.name });
93776
+ }).catch((err) => log67.warn("Failed to persist project", { error: String(err) }));
93777
+ log67.info("Project created", { id: project.id, name: project.name });
92808
93778
  return project;
92809
93779
  }
92810
93780
  getProject(id) {
@@ -92819,14 +93789,14 @@ var init_project_service = __esm({
92819
93789
  if (!project)
92820
93790
  throw new Error(`Project not found: ${id}`);
92821
93791
  Object.assign(project, updates, { updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
92822
- this.projectRepo?.update(id, updates).catch((err) => log65.warn("Failed to persist project update", { error: String(err) }));
92823
- log65.info("Project updated", { id });
93792
+ this.projectRepo?.update(id, updates).catch((err) => log67.warn("Failed to persist project update", { error: String(err) }));
93793
+ log67.info("Project updated", { id });
92824
93794
  return project;
92825
93795
  }
92826
93796
  deleteProject(id) {
92827
93797
  this.projects.delete(id);
92828
- this.projectRepo?.delete(id).catch((err) => log65.warn("Failed to delete project from DB", { error: String(err) }));
92829
- log65.info("Project deleted", { id });
93798
+ this.projectRepo?.delete(id).catch((err) => log67.warn("Failed to delete project from DB", { error: String(err) }));
93799
+ log67.info("Project deleted", { id });
92830
93800
  }
92831
93801
  // ─── Agent Onboarding ──────────────────────────────────────────────────────
92832
93802
  async onboardAgent(agentId2, projectId) {
@@ -92851,7 +93821,7 @@ var init_project_service = __esm({
92851
93821
  parts.push(`- Max pending tasks per agent: ${project.governancePolicy.maxPendingTasksPerAgent}`);
92852
93822
  }
92853
93823
  const onboardingDoc = parts.join("\n");
92854
- log65.info("Agent onboarded to project", { agentId: agentId2, projectId });
93824
+ log67.info("Agent onboarded to project", { agentId: agentId2, projectId });
92855
93825
  return onboardingDoc;
92856
93826
  }
92857
93827
  };
@@ -92859,12 +93829,12 @@ var init_project_service = __esm({
92859
93829
  });
92860
93830
 
92861
93831
  // ../org-manager/dist/requirement-service.js
92862
- var log66, RequirementService;
93832
+ var log68, RequirementService;
92863
93833
  var init_requirement_service = __esm({
92864
93834
  "../org-manager/dist/requirement-service.js"() {
92865
93835
  "use strict";
92866
93836
  init_dist();
92867
- log66 = createLogger("requirement-service");
93837
+ log68 = createLogger("requirement-service");
92868
93838
  RequirementService = class _RequirementService {
92869
93839
  requirements = /* @__PURE__ */ new Map();
92870
93840
  requirementRepo;
@@ -92945,7 +93915,7 @@ var init_requirement_service = __esm({
92945
93915
  reason: reason ?? null
92946
93916
  });
92947
93917
  } catch (e) {
92948
- log66.warn("Failed to record requirement status transition", { reqId, from, to, error: String(e) });
93918
+ log68.warn("Failed to record requirement status transition", { reqId, from, to, error: String(e) });
92949
93919
  }
92950
93920
  }
92951
93921
  getRequirementStatusHistory(reqId, limit = 50) {
@@ -93025,7 +93995,7 @@ var init_requirement_service = __esm({
93025
93995
  approvedBy: req.approvedBy ?? void 0,
93026
93996
  approvedAt: req.approvedAt ? new Date(req.approvedAt) : void 0,
93027
93997
  tags: req.tags
93028
- }).catch((e) => log66.error("Failed to persist requirement", { id: req.id, error: String(e) }));
93998
+ }).catch((e) => log68.error("Failed to persist requirement", { id: req.id, error: String(e) }));
93029
93999
  }
93030
94000
  this.broadcast("requirement:created", req);
93031
94001
  if (this.hitlService && req.source === "agent") {
@@ -93048,10 +94018,10 @@ var init_requirement_service = __esm({
93048
94018
  this.rejectRequirement(req.id, result.respondedBy ?? "hitl", result.comment || "Rejected via approval");
93049
94019
  }
93050
94020
  }).catch((err) => {
93051
- log66.error("HITL approval flow error for requirement", { requirementId: req.id, error: String(err) });
94021
+ log68.error("HITL approval flow error for requirement", { requirementId: req.id, error: String(err) });
93052
94022
  });
93053
94023
  }
93054
- log66.info("Requirement created", {
94024
+ log68.info("Requirement created", {
93055
94025
  id: req.id,
93056
94026
  source: req.source,
93057
94027
  status: req.status,
@@ -93094,11 +94064,11 @@ var init_requirement_service = __esm({
93094
94064
  req.approvedAt = now3;
93095
94065
  req.updatedAt = now3;
93096
94066
  if (this.requirementRepo) {
93097
- this.requirementRepo.approve(id, userId2).catch((e) => log66.error("Failed to persist requirement approval", { id, error: String(e) }));
94067
+ this.requirementRepo.approve(id, userId2).catch((e) => log68.error("Failed to persist requirement approval", { id, error: String(e) }));
93098
94068
  }
93099
94069
  this.recordTransition(id, oldStatus, "in_progress", userId2, "human", "Approved");
93100
94070
  this.broadcast("requirement:approved", req);
93101
- log66.info("Requirement approved", { id, approvedBy: userId2 });
94071
+ log68.info("Requirement approved", { id, approvedBy: userId2 });
93102
94072
  this.notifyCreatorOnDecision(req, "approved", userId2);
93103
94073
  return req;
93104
94074
  }
@@ -93125,11 +94095,11 @@ var init_requirement_service = __esm({
93125
94095
  req.rejectedBy = userId2;
93126
94096
  req.updatedAt = now3;
93127
94097
  if (this.requirementRepo) {
93128
- this.requirementRepo.reject(id, reason, userId2).catch((e) => log66.error("Failed to persist requirement rejection", { id, error: String(e) }));
94098
+ this.requirementRepo.reject(id, reason, userId2).catch((e) => log68.error("Failed to persist requirement rejection", { id, error: String(e) }));
93129
94099
  }
93130
94100
  this.recordTransition(id, oldStatus, "rejected", userId2, "human", reason);
93131
94101
  this.broadcast("requirement:rejected", req);
93132
- log66.info("Requirement rejected", { id, reason });
94102
+ log68.info("Requirement rejected", { id, reason });
93133
94103
  this.notifyCreatorOnDecision(req, "rejected", userId2, reason);
93134
94104
  return req;
93135
94105
  }
@@ -93162,7 +94132,7 @@ var init_requirement_service = __esm({
93162
94132
  req.updatedAt = now3;
93163
94133
  this.recordTransition(id, oldStatus, "pending", req.createdBy, "agent", "Resubmitted");
93164
94134
  if (this.requirementRepo) {
93165
- const persistErr = (e) => log66.error("Failed to persist requirement resubmission", { id, error: String(e) });
94135
+ const persistErr = (e) => log68.error("Failed to persist requirement resubmission", { id, error: String(e) });
93166
94136
  this.requirementRepo.updateStatus(id, "pending").catch(persistErr);
93167
94137
  this.requirementRepo.clearRejectionMetadata(id).catch(persistErr);
93168
94138
  if (updates) {
@@ -93170,7 +94140,7 @@ var init_requirement_service = __esm({
93170
94140
  }
93171
94141
  }
93172
94142
  this.broadcast("requirement:resubmitted", req);
93173
- log66.info("Requirement resubmitted for review", { id, hasUpdates: !!updates });
94143
+ log68.info("Requirement resubmitted for review", { id, hasUpdates: !!updates });
93174
94144
  if (this.hitlService && req.source === "agent") {
93175
94145
  const creatorName = this.resolveAgentName(req.createdBy);
93176
94146
  this.hitlService.requestApprovalAndWait({
@@ -93191,7 +94161,7 @@ var init_requirement_service = __esm({
93191
94161
  this.rejectRequirement(req.id, result.respondedBy ?? "hitl", result.comment || "Rejected via approval");
93192
94162
  }
93193
94163
  }).catch((err) => {
93194
- log66.error("HITL approval flow error for resubmitted requirement", { requirementId: req.id, error: String(err) });
94164
+ log68.error("HITL approval flow error for resubmitted requirement", { requirementId: req.id, error: String(err) });
93195
94165
  });
93196
94166
  }
93197
94167
  return req;
@@ -93227,7 +94197,7 @@ var init_requirement_service = __esm({
93227
94197
  req.rejectedBy = void 0;
93228
94198
  }
93229
94199
  if (this.requirementRepo) {
93230
- const persistErr = (e) => log66.error("Failed to persist requirement status update", { id, error: String(e) });
94200
+ const persistErr = (e) => log68.error("Failed to persist requirement status update", { id, error: String(e) });
93231
94201
  if (newStatus === "in_progress" && oldStatus === "pending") {
93232
94202
  this.requirementRepo.approve(id, req.approvedBy ?? "unknown").catch(persistErr);
93233
94203
  } else if (newStatus === "rejected") {
@@ -93258,7 +94228,7 @@ var init_requirement_service = __esm({
93258
94228
  const resolvedActorType = actorType ?? (userId2 ? "human" : "system");
93259
94229
  this.recordTransition(id, oldStatus, newStatus, userId2, resolvedActorType);
93260
94230
  this.broadcast("requirement:updated", req);
93261
- log66.info("Requirement status updated", { id, from: oldStatus, to: newStatus });
94231
+ log68.info("Requirement status updated", { id, from: oldStatus, to: newStatus });
93262
94232
  return req;
93263
94233
  }
93264
94234
  /**
@@ -93278,7 +94248,7 @@ var init_requirement_service = __esm({
93278
94248
  req.tags = data.tags;
93279
94249
  req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
93280
94250
  if (this.requirementRepo) {
93281
- this.requirementRepo.update(id, data).catch((e) => log66.error("Failed to persist requirement update", { id, error: String(e) }));
94251
+ this.requirementRepo.update(id, data).catch((e) => log68.error("Failed to persist requirement update", { id, error: String(e) }));
93282
94252
  }
93283
94253
  this.broadcast("requirement:updated", req);
93284
94254
  return req;
@@ -93360,11 +94330,11 @@ var init_requirement_service = __esm({
93360
94330
  req.status = "cancelled";
93361
94331
  req.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
93362
94332
  if (this.requirementRepo) {
93363
- this.requirementRepo.updateStatus(id, "cancelled").catch((e) => log66.error("Failed to persist requirement cancellation", { id, error: String(e) }));
94333
+ this.requirementRepo.updateStatus(id, "cancelled").catch((e) => log68.error("Failed to persist requirement cancellation", { id, error: String(e) }));
93364
94334
  }
93365
94335
  this.recordTransition(id, oldStatus, "cancelled", cancelledBy, cancelledByType ?? "system", "Cancelled");
93366
94336
  this.broadcast("requirement:cancelled", req);
93367
- log66.info("Requirement cancelled", { id });
94337
+ log68.info("Requirement cancelled", { id });
93368
94338
  return req;
93369
94339
  }
93370
94340
  getRequirement(id) {
@@ -93421,9 +94391,9 @@ var init_requirement_service = __esm({
93421
94391
  };
93422
94392
  this.requirements.set(req.id, req);
93423
94393
  }
93424
- log66.info("Loaded requirements from storage", { orgId: orgId2, count: rows.length });
94394
+ log68.info("Loaded requirements from storage", { orgId: orgId2, count: rows.length });
93425
94395
  } catch (e) {
93426
- log66.error("Failed to load requirements from storage", { orgId: orgId2, error: String(e) });
94396
+ log68.error("Failed to load requirements from storage", { orgId: orgId2, error: String(e) });
93427
94397
  }
93428
94398
  }
93429
94399
  /**
@@ -93445,7 +94415,7 @@ var init_requirement_service = __esm({
93445
94415
  }
93446
94416
  const linked = [...this.requirements.values()].filter((r) => r.taskIds.length > 0).length;
93447
94417
  if (linked > 0) {
93448
- log66.info("Rebuilt requirement-task links", { linkedRequirements: linked });
94418
+ log68.info("Rebuilt requirement-task links", { linkedRequirements: linked });
93449
94419
  }
93450
94420
  }
93451
94421
  deleteRequirement(id) {
@@ -93454,7 +94424,7 @@ var init_requirement_service = __esm({
93454
94424
  }
93455
94425
  this.requirements.delete(id);
93456
94426
  if (this.requirementRepo) {
93457
- this.requirementRepo.delete(id).catch((e) => log66.error("Failed to delete requirement from storage", { id, error: String(e) }));
94427
+ this.requirementRepo.delete(id).catch((e) => log68.error("Failed to delete requirement from storage", { id, error: String(e) }));
93458
94428
  }
93459
94429
  }
93460
94430
  /**
@@ -93503,7 +94473,7 @@ var init_requirement_service = __esm({
93503
94473
  priority: 1,
93504
94474
  metadata: { senderName: "System", senderRole: "manager" }
93505
94475
  });
93506
- log66.info("Notified creator agent about requirement decision", {
94476
+ log68.info("Notified creator agent about requirement decision", {
93507
94477
  requirementId: req.id,
93508
94478
  creatorId,
93509
94479
  decision
@@ -93548,12 +94518,12 @@ var init_requirement_service = __esm({
93548
94518
  });
93549
94519
 
93550
94520
  // ../org-manager/dist/knowledge-service.js
93551
- var log67, KnowledgeService;
94521
+ var log69, KnowledgeService;
93552
94522
  var init_knowledge_service = __esm({
93553
94523
  "../org-manager/dist/knowledge-service.js"() {
93554
94524
  "use strict";
93555
94525
  init_dist();
93556
- log67 = createLogger("knowledge-service");
94526
+ log69 = createLogger("knowledge-service");
93557
94527
  KnowledgeService = class {
93558
94528
  entries = /* @__PURE__ */ new Map();
93559
94529
  fileStore;
@@ -93563,7 +94533,7 @@ var init_knowledge_service = __esm({
93563
94533
  for (const entry of fileStore.loadAll()) {
93564
94534
  this.entries.set(entry.id, entry);
93565
94535
  }
93566
- log67.info("Knowledge loaded from file store", { count: this.entries.size });
94536
+ log69.info("Knowledge loaded from file store", { count: this.entries.size });
93567
94537
  }
93568
94538
  }
93569
94539
  /** Returns the absolute file path of a knowledge entry (for agent file_read). */
@@ -93608,7 +94578,7 @@ var init_knowledge_service = __esm({
93608
94578
  }
93609
94579
  }
93610
94580
  this.persistScope(entry.scope, entry.scopeId);
93611
- log67.info("Knowledge contributed", {
94581
+ log69.info("Knowledge contributed", {
93612
94582
  id: entry.id,
93613
94583
  scope: entry.scope,
93614
94584
  category: entry.category,
@@ -93686,7 +94656,7 @@ var init_knowledge_service = __esm({
93686
94656
  entry.status = "outdated";
93687
94657
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
93688
94658
  this.persistScope(entry.scope, entry.scopeId);
93689
- log67.info("Knowledge flagged as outdated", { id, reason });
94659
+ log69.info("Knowledge flagged as outdated", { id, reason });
93690
94660
  }
93691
94661
  flagDisputed(id, reason) {
93692
94662
  const entry = this.entries.get(id);
@@ -93695,7 +94665,7 @@ var init_knowledge_service = __esm({
93695
94665
  entry.status = "disputed";
93696
94666
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
93697
94667
  this.persistScope(entry.scope, entry.scopeId);
93698
- log67.info("Knowledge flagged as disputed", { id, reason });
94668
+ log69.info("Knowledge flagged as disputed", { id, reason });
93699
94669
  }
93700
94670
  verify(id, verifiedBy) {
93701
94671
  const entry = this.entries.get(id);
@@ -93705,7 +94675,7 @@ var init_knowledge_service = __esm({
93705
94675
  entry.verifiedBy = verifiedBy;
93706
94676
  entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
93707
94677
  this.persistScope(entry.scope, entry.scopeId);
93708
- log67.info("Knowledge verified", { id, verifiedBy });
94678
+ log69.info("Knowledge verified", { id, verifiedBy });
93709
94679
  }
93710
94680
  // ─── Metrics ───────────────────────────────────────────────────────────────
93711
94681
  getContributions(scopeId, periodStart, periodEnd) {
@@ -93729,8 +94699,8 @@ var init_knowledge_service = __esm({
93729
94699
  });
93730
94700
 
93731
94701
  // ../org-manager/dist/file-knowledge-store.js
93732
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync18, existsSync as existsSync30, mkdirSync as mkdirSync20, readdirSync as readdirSync12, unlinkSync as unlinkSync3 } from "node:fs";
93733
- import { join as join25 } from "node:path";
94702
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync20, existsSync as existsSync32, mkdirSync as mkdirSync22, readdirSync as readdirSync12, unlinkSync as unlinkSync3 } from "node:fs";
94703
+ import { join as join27 } from "node:path";
93734
94704
  function readdirSafe(dir) {
93735
94705
  try {
93736
94706
  return readdirSync12(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
@@ -93738,62 +94708,62 @@ function readdirSafe(dir) {
93738
94708
  return [];
93739
94709
  }
93740
94710
  }
93741
- var log68, FileKnowledgeStore;
94711
+ var log70, FileKnowledgeStore;
93742
94712
  var init_file_knowledge_store = __esm({
93743
94713
  "../org-manager/dist/file-knowledge-store.js"() {
93744
94714
  "use strict";
93745
94715
  init_dist();
93746
- log68 = createLogger("file-knowledge-store");
94716
+ log70 = createLogger("file-knowledge-store");
93747
94717
  FileKnowledgeStore = class {
93748
94718
  baseDir;
93749
94719
  constructor(baseDir) {
93750
94720
  this.baseDir = baseDir;
93751
- mkdirSync20(baseDir, { recursive: true });
94721
+ mkdirSync22(baseDir, { recursive: true });
93752
94722
  }
93753
94723
  scopeDir(scope, scopeId) {
93754
- return join25(this.baseDir, scope, scopeId);
94724
+ return join27(this.baseDir, scope, scopeId);
93755
94725
  }
93756
94726
  indexPath(scope, scopeId) {
93757
- return join25(this.scopeDir(scope, scopeId), "_index.json");
94727
+ return join27(this.scopeDir(scope, scopeId), "_index.json");
93758
94728
  }
93759
94729
  entryPath(entry) {
93760
- return join25(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
94730
+ return join27(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
93761
94731
  }
93762
94732
  // ─── Load ────────────────────────────────────────────────────────────────
93763
94733
  loadAll() {
93764
94734
  const entries2 = [];
93765
- if (!existsSync30(this.baseDir))
94735
+ if (!existsSync32(this.baseDir))
93766
94736
  return entries2;
93767
94737
  for (const scope of readdirSafe(this.baseDir)) {
93768
- const scopePath = join25(this.baseDir, scope);
94738
+ const scopePath = join27(this.baseDir, scope);
93769
94739
  for (const scopeId of readdirSafe(scopePath)) {
93770
- const idxPath = join25(scopePath, scopeId, "_index.json");
93771
- if (!existsSync30(idxPath))
94740
+ const idxPath = join27(scopePath, scopeId, "_index.json");
94741
+ if (!existsSync32(idxPath))
93772
94742
  continue;
93773
94743
  try {
93774
- const data = JSON.parse(readFileSync23(idxPath, "utf-8"));
94744
+ const data = JSON.parse(readFileSync25(idxPath, "utf-8"));
93775
94745
  entries2.push(...data);
93776
94746
  } catch (err) {
93777
- log68.warn("Failed to load index", { path: idxPath, error: String(err) });
94747
+ log70.warn("Failed to load index", { path: idxPath, error: String(err) });
93778
94748
  }
93779
94749
  }
93780
94750
  }
93781
- log68.info("Knowledge loaded from disk", { count: entries2.length });
94751
+ log70.info("Knowledge loaded from disk", { count: entries2.length });
93782
94752
  return entries2;
93783
94753
  }
93784
94754
  // ─── Write ───────────────────────────────────────────────────────────────
93785
94755
  saveEntry(entry) {
93786
94756
  const dir = this.scopeDir(entry.scope, entry.scopeId);
93787
- mkdirSync20(dir, { recursive: true });
93788
- writeFileSync18(join25(dir, `${entry.id}.json`), JSON.stringify(entry, null, 2));
94757
+ mkdirSync22(dir, { recursive: true });
94758
+ writeFileSync20(join27(dir, `${entry.id}.json`), JSON.stringify(entry, null, 2));
93789
94759
  }
93790
94760
  saveIndex(scope, scopeId, entries2) {
93791
94761
  const dir = this.scopeDir(scope, scopeId);
93792
- mkdirSync20(dir, { recursive: true });
93793
- writeFileSync18(join25(dir, "_index.json"), JSON.stringify(entries2, null, 2));
94762
+ mkdirSync22(dir, { recursive: true });
94763
+ writeFileSync20(join27(dir, "_index.json"), JSON.stringify(entries2, null, 2));
93794
94764
  }
93795
94765
  removeEntryFile(entry) {
93796
- const p = join25(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
94766
+ const p = join27(this.scopeDir(entry.scope, entry.scopeId), `${entry.id}.json`);
93797
94767
  try {
93798
94768
  unlinkSync3(p);
93799
94769
  } catch {
@@ -93816,17 +94786,17 @@ var init_file_knowledge_store = __esm({
93816
94786
  });
93817
94787
 
93818
94788
  // ../org-manager/dist/deliverable-service.js
93819
- import { existsSync as existsSync31, cpSync as cpSync3, mkdirSync as mkdirSync21 } from "node:fs";
93820
- import { join as join26, basename } from "node:path";
94789
+ import { existsSync as existsSync33, cpSync as cpSync3, mkdirSync as mkdirSync23 } from "node:fs";
94790
+ import { join as join28, basename } from "node:path";
93821
94791
  function isUrl(s2) {
93822
94792
  return /^https?:\/\//i.test(s2);
93823
94793
  }
93824
- var log69, DeliverableService;
94794
+ var log71, DeliverableService;
93825
94795
  var init_deliverable_service = __esm({
93826
94796
  "../org-manager/dist/deliverable-service.js"() {
93827
94797
  "use strict";
93828
94798
  init_dist();
93829
- log69 = createLogger("deliverable-service");
94799
+ log71 = createLogger("deliverable-service");
93830
94800
  DeliverableService = class {
93831
94801
  repo;
93832
94802
  cache = /* @__PURE__ */ new Map();
@@ -93844,7 +94814,7 @@ var init_deliverable_service = __esm({
93844
94814
  for (const r of rows) {
93845
94815
  this.cache.set(r.id, this.rowToDeliverable(r));
93846
94816
  }
93847
- log69.info("Deliverables loaded", { count: this.cache.size });
94817
+ log71.info("Deliverables loaded", { count: this.cache.size });
93848
94818
  }
93849
94819
  async create(opts) {
93850
94820
  const ref = opts.reference?.trim();
@@ -93870,7 +94840,7 @@ var init_deliverable_service = __esm({
93870
94840
  if (opts.taskId && !existing.taskId)
93871
94841
  patch.taskId = opts.taskId;
93872
94842
  const updated = await this.update(existing.id, patch);
93873
- log69.info("Deliverable upserted (updated existing)", { id: existing.id, reference: ref });
94843
+ log71.info("Deliverable upserted (updated existing)", { id: existing.id, reference: ref });
93874
94844
  this.ws?.broadcastDeliverableUpdate(existing.id, "updated", {
93875
94845
  type: opts.type,
93876
94846
  title: opts.title,
@@ -93923,7 +94893,7 @@ var init_deliverable_service = __esm({
93923
94893
  testResults: opts.testResults
93924
94894
  });
93925
94895
  this.cache.set(id, deliverable);
93926
- log69.info("Deliverable created", { id, type: opts.type, title: opts.title });
94896
+ log71.info("Deliverable created", { id, type: opts.type, title: opts.title });
93927
94897
  this.ws?.broadcastDeliverableUpdate(id, "created", {
93928
94898
  type: opts.type,
93929
94899
  title: opts.title,
@@ -94020,7 +94990,7 @@ var init_deliverable_service = __esm({
94020
94990
  if (data.testResults !== void 0 && !arrEq(data.testResults, d.testResults))
94021
94991
  changed.push("testResults");
94022
94992
  if (changed.length === 0) {
94023
- log69.debug("Deliverable update skipped (no-op)", { id });
94993
+ log71.debug("Deliverable update skipped (no-op)", { id });
94024
94994
  return d;
94025
94995
  }
94026
94996
  const now3 = (/* @__PURE__ */ new Date()).toISOString();
@@ -94054,7 +95024,7 @@ var init_deliverable_service = __esm({
94054
95024
  d.testResults = data.testResults;
94055
95025
  d.updatedAt = now3;
94056
95026
  await this.repo?.update(id, data);
94057
- log69.info("Deliverable updated", { id, fields: changed });
95027
+ log71.info("Deliverable updated", { id, fields: changed });
94058
95028
  this.ws?.broadcastDeliverableUpdate(id, "updated", {
94059
95029
  type: d.type,
94060
95030
  title: d.title,
@@ -94071,7 +95041,7 @@ var init_deliverable_service = __esm({
94071
95041
  d.status = "outdated";
94072
95042
  d.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
94073
95043
  await this.repo?.update(id, { status: "outdated" });
94074
- log69.info("Deliverable flagged outdated", { id });
95044
+ log71.info("Deliverable flagged outdated", { id });
94075
95045
  this.ws?.broadcastDeliverableUpdate(id, "removed", {
94076
95046
  type: d.type,
94077
95047
  title: d.title,
@@ -94133,7 +95103,7 @@ var init_deliverable_service = __esm({
94133
95103
  }
94134
95104
  }
94135
95105
  if (cleaned > 0) {
94136
- log69.info("Deduplicated deliverables by reference", { cleaned });
95106
+ log71.info("Deduplicated deliverables by reference", { cleaned });
94137
95107
  }
94138
95108
  return cleaned;
94139
95109
  }
@@ -94146,30 +95116,30 @@ var init_deliverable_service = __esm({
94146
95116
  const deliverables = this.findByAgent(agentId2);
94147
95117
  if (deliverables.length === 0)
94148
95118
  return 0;
94149
- const sharedDeliverables = join26(sharedDataDir, "deliverables");
95119
+ const sharedDeliverables = join28(sharedDataDir, "deliverables");
94150
95120
  let migrated = 0;
94151
95121
  for (const d of deliverables) {
94152
95122
  if (!d.reference || isUrl(d.reference))
94153
95123
  continue;
94154
95124
  if (!d.reference.startsWith(agentDir + "/") && d.reference !== agentDir)
94155
95125
  continue;
94156
- if (!existsSync31(d.reference))
95126
+ if (!existsSync33(d.reference))
94157
95127
  continue;
94158
95128
  try {
94159
- const destDir = join26(sharedDeliverables, d.id);
94160
- mkdirSync21(destDir, { recursive: true });
95129
+ const destDir = join28(sharedDeliverables, d.id);
95130
+ mkdirSync23(destDir, { recursive: true });
94161
95131
  const fileName = basename(d.reference);
94162
- const destPath = join26(destDir, fileName);
95132
+ const destPath = join28(destDir, fileName);
94163
95133
  cpSync3(d.reference, destPath, { recursive: true });
94164
95134
  await this.update(d.id, { reference: destPath });
94165
95135
  migrated++;
94166
- log69.info("Deliverable file migrated to shared", { id: d.id, from: d.reference, to: destPath });
95136
+ log71.info("Deliverable file migrated to shared", { id: d.id, from: d.reference, to: destPath });
94167
95137
  } catch (err) {
94168
- log69.warn("Failed to migrate deliverable file", { id: d.id, reference: d.reference, error: String(err) });
95138
+ log71.warn("Failed to migrate deliverable file", { id: d.id, reference: d.reference, error: String(err) });
94169
95139
  }
94170
95140
  }
94171
95141
  if (migrated > 0) {
94172
- log69.info("Migrated agent deliverable files to shared directory", { agentId: agentId2, migrated, total: deliverables.length });
95142
+ log71.info("Migrated agent deliverable files to shared directory", { agentId: agentId2, migrated, total: deliverables.length });
94173
95143
  }
94174
95144
  return migrated;
94175
95145
  }
@@ -94183,7 +95153,7 @@ var init_deliverable_service = __esm({
94183
95153
  for (const d of deliverables) {
94184
95154
  if (!d.reference || isUrl(d.reference))
94185
95155
  continue;
94186
- if (!existsSync31(d.reference)) {
95156
+ if (!existsSync33(d.reference)) {
94187
95157
  missing.push(d.id);
94188
95158
  }
94189
95159
  }
@@ -94205,7 +95175,7 @@ var init_deliverable_service = __esm({
94205
95175
  }
94206
95176
  }
94207
95177
  if (branchCleaned > 0) {
94208
- log69.info("Cleaned up legacy branch-type deliverables", { count: branchCleaned });
95178
+ log71.info("Cleaned up legacy branch-type deliverables", { count: branchCleaned });
94209
95179
  }
94210
95180
  const existingTaskIds = this.repo ? await this.repo.listTaskIdsWithDeliverables() : new Set([...this.cache.values()].map((d) => d.taskId).filter(Boolean));
94211
95181
  let migrated = 0;
@@ -94232,12 +95202,12 @@ var init_deliverable_service = __esm({
94232
95202
  });
94233
95203
  migrated++;
94234
95204
  } catch (err) {
94235
- log69.warn("Failed to migrate task deliverable", { taskId: task.id, ref: d.reference, error: String(err) });
95205
+ log71.warn("Failed to migrate task deliverable", { taskId: task.id, ref: d.reference, error: String(err) });
94236
95206
  }
94237
95207
  }
94238
95208
  }
94239
95209
  if (migrated > 0) {
94240
- log69.info("Migrated task deliverables to unified table", { migrated });
95210
+ log71.info("Migrated task deliverables to unified table", { migrated });
94241
95211
  }
94242
95212
  return migrated;
94243
95213
  }
@@ -94292,12 +95262,12 @@ var init_deliverable_service = __esm({
94292
95262
  });
94293
95263
 
94294
95264
  // ../org-manager/dist/report-service.js
94295
- var log70, ReportService;
95265
+ var log72, ReportService;
94296
95266
  var init_report_service = __esm({
94297
95267
  "../org-manager/dist/report-service.js"() {
94298
95268
  "use strict";
94299
95269
  init_dist();
94300
- log70 = createLogger("report-service");
95270
+ log72 = createLogger("report-service");
94301
95271
  ReportService = class {
94302
95272
  taskService;
94303
95273
  billingService;
@@ -94350,7 +95320,7 @@ var init_report_service = __esm({
94350
95320
  generatedBy: opts.generatedBy ?? "system"
94351
95321
  };
94352
95322
  this.reports.set(report.id, report);
94353
- log70.info("Report generated", { id: report.id, type: report.type, scope: report.scope });
95323
+ log72.info("Report generated", { id: report.id, type: report.type, scope: report.scope });
94354
95324
  return report;
94355
95325
  }
94356
95326
  getReport(id) {
@@ -94372,7 +95342,7 @@ var init_report_service = __esm({
94372
95342
  if (!report?.upcomingPlan)
94373
95343
  throw new Error("Report has no plan");
94374
95344
  report.upcomingPlan.status = "pending";
94375
- log70.info("Plan submitted for approval", { reportId });
95345
+ log72.info("Plan submitted for approval", { reportId });
94376
95346
  }
94377
95347
  approvePlan(reportId, userId2) {
94378
95348
  const report = this.reports.get(reportId);
@@ -94399,7 +95369,7 @@ var init_report_service = __esm({
94399
95369
  projectId: report.scope === "project" ? report.scopeId : void 0
94400
95370
  });
94401
95371
  }
94402
- log70.info("Plan approved \u2014 tasks created", {
95372
+ log72.info("Plan approved \u2014 tasks created", {
94403
95373
  reportId,
94404
95374
  taskCount: report.upcomingPlan.plannedTasks.length
94405
95375
  });
@@ -94411,7 +95381,7 @@ var init_report_service = __esm({
94411
95381
  throw new Error("Report has no plan");
94412
95382
  report.upcomingPlan.status = "rejected";
94413
95383
  report.upcomingPlan.rejectionReason = reason;
94414
- log70.info("Plan rejected", { reportId, reason });
95384
+ log72.info("Plan rejected", { reportId, reason });
94415
95385
  return report;
94416
95386
  }
94417
95387
  // ─── Feedback ──────────────────────────────────────────────────────────────
@@ -94463,7 +95433,7 @@ var init_report_service = __esm({
94463
95433
  const existing = this.feedbackStore.get(opts.reportId) ?? [];
94464
95434
  existing.push(feedback);
94465
95435
  this.feedbackStore.set(opts.reportId, existing);
94466
- log70.info("Feedback added to report", {
95436
+ log72.info("Feedback added to report", {
94467
95437
  reportId: opts.reportId,
94468
95438
  type: opts.type,
94469
95439
  disclosure: opts.disclosure.scope
@@ -94548,12 +95518,12 @@ var init_report_service = __esm({
94548
95518
  });
94549
95519
 
94550
95520
  // ../org-manager/dist/trust-service.js
94551
- var log71, TrustService;
95521
+ var log73, TrustService;
94552
95522
  var init_trust_service = __esm({
94553
95523
  "../org-manager/dist/trust-service.js"() {
94554
95524
  "use strict";
94555
95525
  init_dist();
94556
- log71 = createLogger("trust-service");
95526
+ log73 = createLogger("trust-service");
94557
95527
  TrustService = class {
94558
95528
  trustLevels = /* @__PURE__ */ new Map();
94559
95529
  getOrCreate(agentId2) {
@@ -94602,7 +95572,7 @@ var init_trust_service = __esm({
94602
95572
  trust.level = this.scoreToLevel(trust.score, trust.totalDeliveries);
94603
95573
  trust.lastEvaluatedAt = (/* @__PURE__ */ new Date()).toISOString();
94604
95574
  if (trust.level !== oldLevel) {
94605
- log71.info("Trust level changed", {
95575
+ log73.info("Trust level changed", {
94606
95576
  agentId: trust.agentId,
94607
95577
  oldLevel,
94608
95578
  newLevel: trust.level,
@@ -94647,12 +95617,12 @@ var init_trust_service = __esm({
94647
95617
  });
94648
95618
 
94649
95619
  // ../org-manager/dist/archive-service.js
94650
- var log72, ARCHIVABLE_STATUSES, ACTIVE_DISCUSSION_DAYS, ACTIVE_DISCUSSION_MS, ArchiveService;
95620
+ var log74, ARCHIVABLE_STATUSES, ACTIVE_DISCUSSION_DAYS, ACTIVE_DISCUSSION_MS, ArchiveService;
94651
95621
  var init_archive_service = __esm({
94652
95622
  "../org-manager/dist/archive-service.js"() {
94653
95623
  "use strict";
94654
95624
  init_dist();
94655
- log72 = createLogger("archive-service");
95625
+ log74 = createLogger("archive-service");
94656
95626
  ARCHIVABLE_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "rejected", "cancelled"]);
94657
95627
  ACTIVE_DISCUSSION_DAYS = 7;
94658
95628
  ACTIVE_DISCUSSION_MS = ACTIVE_DISCUSSION_DAYS * 864e5;
@@ -94673,11 +95643,11 @@ var init_archive_service = __esm({
94673
95643
  * then repeats at the configured interval.
94674
95644
  */
94675
95645
  start(intervalMs = ARCHIVE_SCAN_INTERVAL_MS) {
94676
- this.runArchiveScan().catch((err) => log72.warn("Initial archive scan failed", { error: String(err) }));
95646
+ this.runArchiveScan().catch((err) => log74.warn("Initial archive scan failed", { error: String(err) }));
94677
95647
  this.scanInterval = setInterval(() => {
94678
- this.runArchiveScan().catch((err) => log72.warn("Archive scan failed", { error: String(err) }));
95648
+ this.runArchiveScan().catch((err) => log74.warn("Archive scan failed", { error: String(err) }));
94679
95649
  }, intervalMs);
94680
- log72.info("Archive service started", { intervalMs });
95650
+ log74.info("Archive service started", { intervalMs });
94681
95651
  }
94682
95652
  stop() {
94683
95653
  if (this.scanInterval) {
@@ -94689,7 +95659,7 @@ var init_archive_service = __esm({
94689
95659
  const archivedTasks = await this.archiveTasks();
94690
95660
  const archivedRequirements = this.archiveRequirements();
94691
95661
  if (archivedTasks > 0 || archivedRequirements > 0) {
94692
- log72.info("Archive scan complete", { archivedTasks, archivedRequirements });
95662
+ log74.info("Archive scan complete", { archivedTasks, archivedRequirements });
94693
95663
  }
94694
95664
  return { archivedTasks, archivedRequirements };
94695
95665
  }
@@ -94712,7 +95682,7 @@ var init_archive_service = __esm({
94712
95682
  this.taskService.archiveTask(task.id);
94713
95683
  archived++;
94714
95684
  } catch (err) {
94715
- log72.warn("Failed to archive task", { taskId: task.id, error: String(err) });
95685
+ log74.warn("Failed to archive task", { taskId: task.id, error: String(err) });
94716
95686
  }
94717
95687
  }
94718
95688
  }
@@ -94737,7 +95707,7 @@ var init_archive_service = __esm({
94737
95707
  this.requirementService.updateRequirementStatus(req.id, "archived");
94738
95708
  archived++;
94739
95709
  } catch (err) {
94740
- log72.warn("Failed to archive requirement", { requirementId: req.id, error: String(err) });
95710
+ log74.warn("Failed to archive requirement", { requirementId: req.id, error: String(err) });
94741
95711
  }
94742
95712
  }
94743
95713
  }
@@ -94779,12 +95749,12 @@ var init_archive_service = __esm({
94779
95749
  });
94780
95750
 
94781
95751
  // ../org-manager/dist/stale-detector.js
94782
- var log73, DEFAULT_CONFIG4, StaleDetector;
95752
+ var log75, DEFAULT_CONFIG4, StaleDetector;
94783
95753
  var init_stale_detector = __esm({
94784
95754
  "../org-manager/dist/stale-detector.js"() {
94785
95755
  "use strict";
94786
95756
  init_dist();
94787
- log73 = createLogger("stale-detector");
95757
+ log75 = createLogger("stale-detector");
94788
95758
  DEFAULT_CONFIG4 = {
94789
95759
  maxInProgressMs: 24 * 60 * 60 * 1e3,
94790
95760
  maxReviewWaitMs: 12 * 60 * 60 * 1e3,
@@ -94807,9 +95777,9 @@ var init_stale_detector = __esm({
94807
95777
  if (items.length > 0 && this.onStaleItems) {
94808
95778
  this.onStaleItems(items);
94809
95779
  }
94810
- }).catch((err) => log73.warn("Stale scan failed", { error: String(err) }));
95780
+ }).catch((err) => log75.warn("Stale scan failed", { error: String(err) }));
94811
95781
  }, intervalMs);
94812
- log73.info("Stale detector started", { intervalMs });
95782
+ log75.info("Stale detector started", { intervalMs });
94813
95783
  }
94814
95784
  stop() {
94815
95785
  if (this.scanInterval) {
@@ -94852,7 +95822,7 @@ var init_stale_detector = __esm({
94852
95822
  }
94853
95823
  }
94854
95824
  if (staleItems.length > 0) {
94855
- log73.info(`Found ${staleItems.length} stale items`);
95825
+ log75.info(`Found ${staleItems.length} stale items`);
94856
95826
  }
94857
95827
  return staleItems;
94858
95828
  }
@@ -94861,12 +95831,12 @@ var init_stale_detector = __esm({
94861
95831
  });
94862
95832
 
94863
95833
  // ../org-manager/dist/scheduled-task-runner.js
94864
- var log74, MIN_STAGGER_MS, MAX_STAGGER_MS, ScheduledTaskRunner;
95834
+ var log76, MIN_STAGGER_MS, MAX_STAGGER_MS, ScheduledTaskRunner;
94865
95835
  var init_scheduled_task_runner = __esm({
94866
95836
  "../org-manager/dist/scheduled-task-runner.js"() {
94867
95837
  "use strict";
94868
95838
  init_dist();
94869
- log74 = createLogger("scheduled-task-runner");
95839
+ log76 = createLogger("scheduled-task-runner");
94870
95840
  MIN_STAGGER_MS = 2 * 6e4;
94871
95841
  MAX_STAGGER_MS = 15 * 6e4;
94872
95842
  ScheduledTaskRunner = class {
@@ -94885,11 +95855,11 @@ var init_scheduled_task_runner = __esm({
94885
95855
  return;
94886
95856
  this.running = true;
94887
95857
  this.startedAt = Date.now();
94888
- this.tick().catch((e) => log74.error("Initial scheduled task tick failed", { error: String(e) }));
95858
+ this.tick().catch((e) => log76.error("Initial scheduled task tick failed", { error: String(e) }));
94889
95859
  this.timer = setInterval(() => {
94890
- this.tick().catch((e) => log74.error("Scheduled task tick failed", { error: String(e) }));
95860
+ this.tick().catch((e) => log76.error("Scheduled task tick failed", { error: String(e) }));
94891
95861
  }, this.pollIntervalMs);
94892
- log74.info("ScheduledTaskRunner started", { pollIntervalMs: this.pollIntervalMs });
95862
+ log76.info("ScheduledTaskRunner started", { pollIntervalMs: this.pollIntervalMs });
94893
95863
  }
94894
95864
  stop() {
94895
95865
  if (this.timer) {
@@ -94900,7 +95870,7 @@ var init_scheduled_task_runner = __esm({
94900
95870
  clearTimeout(t);
94901
95871
  this.staggerTimers = [];
94902
95872
  this.running = false;
94903
- log74.info("ScheduledTaskRunner stopped");
95873
+ log76.info("ScheduledTaskRunner stopped");
94904
95874
  }
94905
95875
  isRunning() {
94906
95876
  return this.running;
@@ -94934,7 +95904,7 @@ var init_scheduled_task_runner = __esm({
94934
95904
  }
94935
95905
  if (startup && nextRun < this.startedAt) {
94936
95906
  const delay = MIN_STAGGER_MS + Math.random() * (MAX_STAGGER_MS - MIN_STAGGER_MS);
94937
- log74.info("Staggering overdue scheduled task", {
95907
+ log76.info("Staggering overdue scheduled task", {
94938
95908
  taskId: task.id,
94939
95909
  title: task.title,
94940
95910
  overdueBy: `${Math.round((now3 - nextRun) / 6e4)}m`,
@@ -94943,7 +95913,7 @@ var init_scheduled_task_runner = __esm({
94943
95913
  const timer = setTimeout(() => {
94944
95914
  if (!this.running)
94945
95915
  return;
94946
- this.fireScheduledTask(task).catch((e) => log74.error("Failed to fire staggered scheduled task", { taskId: task.id, error: String(e) }));
95916
+ this.fireScheduledTask(task).catch((e) => log76.error("Failed to fire staggered scheduled task", { taskId: task.id, error: String(e) }));
94947
95917
  }, delay);
94948
95918
  this.staggerTimers.push(timer);
94949
95919
  continue;
@@ -94951,27 +95921,27 @@ var init_scheduled_task_runner = __esm({
94951
95921
  try {
94952
95922
  await this.fireScheduledTask(task);
94953
95923
  } catch (e) {
94954
- log74.error("Failed to fire scheduled task", { taskId: task.id, error: String(e) });
95924
+ log76.error("Failed to fire scheduled task", { taskId: task.id, error: String(e) });
94955
95925
  }
94956
95926
  }
94957
95927
  }
94958
95928
  async fireScheduledTask(task) {
94959
- log74.info("Firing scheduled task", { taskId: task.id, title: task.title });
95929
+ log76.info("Firing scheduled task", { taskId: task.id, title: task.title });
94960
95930
  await this.taskService.advanceScheduleConfig(task.id);
94961
95931
  const resettableStatuses = ["completed", "cancelled", "failed"];
94962
95932
  if (resettableStatuses.includes(task.status)) {
94963
95933
  await this.taskService.resetTaskForRerun(task.id);
94964
95934
  } else if (!["in_progress", "review", "blocked", "pending"].includes(task.status)) {
94965
- log74.warn("Scheduled task has unexpected status, resetting for rerun", { taskId: task.id, status: task.status });
95935
+ log76.warn("Scheduled task has unexpected status, resetting for rerun", { taskId: task.id, status: task.status });
94966
95936
  await this.taskService.resetTaskForRerun(task.id);
94967
95937
  }
94968
95938
  const current = this.taskService.getTask(task.id);
94969
95939
  if (current && current.status === "in_progress") {
94970
95940
  try {
94971
95941
  await this.taskService.runTask(task.id);
94972
- log74.info("Scheduled task auto-started", { taskId: task.id });
95942
+ log76.info("Scheduled task auto-started", { taskId: task.id });
94973
95943
  } catch (err) {
94974
- log74.warn("Failed to auto-start scheduled task (agent may be busy)", { taskId: task.id, error: String(err) });
95944
+ log76.warn("Failed to auto-start scheduled task (agent may be busy)", { taskId: task.id, error: String(err) });
94975
95945
  }
94976
95946
  }
94977
95947
  }
@@ -94981,11 +95951,11 @@ var init_scheduled_task_runner = __esm({
94981
95951
 
94982
95952
  // ../storage/dist/sqlite-storage.js
94983
95953
  import { DatabaseSync } from "node:sqlite";
94984
- import { randomUUID } from "node:crypto";
94985
- import { mkdirSync as mkdirSync22 } from "node:fs";
94986
- import { dirname as dirname9 } from "node:path";
95954
+ import { randomUUID as randomUUID2 } from "node:crypto";
95955
+ import { mkdirSync as mkdirSync24 } from "node:fs";
95956
+ import { dirname as dirname11 } from "node:path";
94987
95957
  function generateId2(prefix = "") {
94988
- const uuid = randomUUID().replace(/-/g, "").slice(0, 16);
95958
+ const uuid = randomUUID2().replace(/-/g, "").slice(0, 16);
94989
95959
  return prefix ? `${prefix}_${uuid}` : uuid;
94990
95960
  }
94991
95961
  function now2() {
@@ -95003,7 +95973,7 @@ function toDate(v) {
95003
95973
  function openSqlite(dbPath) {
95004
95974
  if (_db)
95005
95975
  return _db;
95006
- mkdirSync22(dirname9(dbPath), { recursive: true });
95976
+ mkdirSync24(dirname11(dbPath), { recursive: true });
95007
95977
  _db = new DatabaseSync(dbPath);
95008
95978
  _db.exec("PRAGMA journal_mode = WAL");
95009
95979
  _db.exec("PRAGMA foreign_keys = ON");
@@ -95047,6 +96017,8 @@ function openSqlite(dbPath) {
95047
96017
  { table: "projects", column: "created_by", sql: "ALTER TABLE projects ADD COLUMN created_by TEXT" },
95048
96018
  { table: "approvals", column: "target_user_id", sql: "ALTER TABLE approvals ADD COLUMN target_user_id TEXT" },
95049
96019
  { table: "users", column: "deleted_at", sql: "ALTER TABLE users ADD COLUMN deleted_at TEXT" },
96020
+ { table: "users", column: "hub_user_id", sql: "ALTER TABLE users ADD COLUMN hub_user_id TEXT" },
96021
+ { table: "users", column: "hub_username", sql: "ALTER TABLE users ADD COLUMN hub_username TEXT" },
95050
96022
  { table: "agents", column: "deleted_at", sql: "ALTER TABLE agents ADD COLUMN deleted_at TEXT" },
95051
96023
  { table: "deliverables", column: "format", sql: "ALTER TABLE deliverables ADD COLUMN format TEXT" },
95052
96024
  { table: "task_comments", column: "reply_to_id", sql: "ALTER TABLE task_comments ADD COLUMN reply_to_id TEXT" },
@@ -95056,7 +96028,7 @@ function openSqlite(dbPath) {
95056
96028
  const cols = _db.prepare(`PRAGMA table_info(${m.table})`).all();
95057
96029
  if (!cols.some((c) => c.name === m.column)) {
95058
96030
  _db.exec(m.sql);
95059
- log75.info(`Migration: added column ${m.column} to ${m.table}`);
96031
+ log77.info(`Migration: added column ${m.column} to ${m.table}`);
95060
96032
  }
95061
96033
  }
95062
96034
  _db.exec("CREATE INDEX IF NOT EXISTS idx_agent_activities_mailbox ON agent_activities(mailbox_item_id)");
@@ -95070,10 +96042,10 @@ function openSqlite(dbPath) {
95070
96042
  for (const m of statusMigrations) {
95071
96043
  const result = _db.prepare(m.sql).run();
95072
96044
  if (result.changes > 0) {
95073
- log75.info(`Status migration: ${m.desc} (${result.changes} rows)`);
96045
+ log77.info(`Status migration: ${m.desc} (${result.changes} rows)`);
95074
96046
  }
95075
96047
  }
95076
- log75.info("SQLite database opened", { path: dbPath });
96048
+ log77.info("SQLite database opened", { path: dbPath });
95077
96049
  return _db;
95078
96050
  }
95079
96051
  function closeSqlite() {
@@ -95093,7 +96065,7 @@ function migrateToExecutionStreamLogs(db) {
95093
96065
  SELECT id, 'task', task_id, agent_id, seq, type, content, metadata, execution_round, created_at
95094
96066
  FROM task_logs
95095
96067
  `);
95096
- log75.info(`Migration: copied ${taskLogCount} task_logs to execution_stream_logs`);
96068
+ log77.info(`Migration: copied ${taskLogCount} task_logs to execution_stream_logs`);
95097
96069
  }
95098
96070
  const actLogCount = db.prepare("SELECT COUNT(*) as cnt FROM agent_activity_logs").get().cnt;
95099
96071
  if (actLogCount > 0) {
@@ -95104,15 +96076,15 @@ function migrateToExecutionStreamLogs(db) {
95104
96076
  seq, type, content, metadata, NULL, created_at
95105
96077
  FROM agent_activity_logs
95106
96078
  `);
95107
- log75.info(`Migration: copied ${actLogCount} agent_activity_logs to execution_stream_logs`);
96079
+ log77.info(`Migration: copied ${actLogCount} agent_activity_logs to execution_stream_logs`);
95108
96080
  }
95109
96081
  }
95110
- var log75, SCHEMA_SQL, _db, SqliteOrgRepo, SqliteAgentRepo, SqliteTaskRepo, SqliteRequirementRepo, SqliteProjectRepo, SqliteAuditRepo, SqliteTaskLogRepo, SqliteTaskCommentRepo, SqliteRequirementCommentRepo, SqliteMessageRepo, SqliteChatSessionRepo, SqliteChannelMessageRepo, SqliteUserRepo, SqliteTeamRepo, SqliteMarketplaceTemplateRepo, SqliteMarketplaceSkillRepo, SqliteMarketplaceRatingRepo, SqliteAgentKnowledgeRepo, SqliteExternalAgentRepo, SqliteDeliverableRepo, SqliteActivityRepo, SqliteExecutionStreamRepo, SqliteMailboxRepo, SqliteDecisionRepo, SqliteNotificationRepo, SqliteApprovalRepo, SqliteGroupChatRepo, SqliteStatusTransitionRepo, SqliteReadCursorRepo;
96082
+ var log77, SCHEMA_SQL, _db, SqliteOrgRepo, SqliteAgentRepo, SqliteTaskRepo, SqliteRequirementRepo, SqliteProjectRepo, SqliteAuditRepo, SqliteTaskLogRepo, SqliteTaskCommentRepo, SqliteRequirementCommentRepo, SqliteMessageRepo, SqliteChatSessionRepo, SqliteChannelMessageRepo, SqliteUserRepo, SqliteTeamRepo, SqliteMarketplaceTemplateRepo, SqliteMarketplaceSkillRepo, SqliteMarketplaceRatingRepo, SqliteAgentKnowledgeRepo, SqliteExternalAgentRepo, SqliteDeliverableRepo, SqliteActivityRepo, SqliteExecutionStreamRepo, SqliteMailboxRepo, SqliteDecisionRepo, SqliteNotificationRepo, SqliteApprovalRepo, SqliteGroupChatRepo, SqliteStatusTransitionRepo, SqliteReadCursorRepo;
95111
96083
  var init_sqlite_storage = __esm({
95112
96084
  "../storage/dist/sqlite-storage.js"() {
95113
96085
  "use strict";
95114
96086
  init_dist();
95115
- log75 = createLogger("sqlite-storage");
96087
+ log77 = createLogger("sqlite-storage");
95116
96088
  SCHEMA_SQL = `
95117
96089
  CREATE TABLE IF NOT EXISTS organizations (
95118
96090
  id TEXT PRIMARY KEY,
@@ -96594,7 +97566,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96594
97566
  migrated++;
96595
97567
  }
96596
97568
  if (migrated > 0) {
96597
- log75.info(`Migrated ${migrated} legacy chat messages to segment format`);
97569
+ log77.info(`Migrated ${migrated} legacy chat messages to segment format`);
96598
97570
  }
96599
97571
  return migrated;
96600
97572
  }
@@ -96606,7 +97578,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96606
97578
  const result = this.db.prepare("UPDATE chat_sessions SET user_id = ? WHERE user_id IS NULL").run(defaultUserId);
96607
97579
  const count = Number(result.changes);
96608
97580
  if (count > 0) {
96609
- log75.info(`Migrated ${count} chat sessions with NULL user_id to user ${defaultUserId}`);
97581
+ log77.info(`Migrated ${count} chat sessions with NULL user_id to user ${defaultUserId}`);
96610
97582
  }
96611
97583
  return count;
96612
97584
  }
@@ -96620,7 +97592,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96620
97592
  const result = this.db.prepare("UPDATE chat_sessions SET user_id = ? WHERE user_id = 'default'").run(realOwnerId);
96621
97593
  const count = Number(result.changes);
96622
97594
  if (count > 0) {
96623
- log75.info(`Migrated ${count} chat sessions from user_id='default' to ${realOwnerId}`);
97595
+ log77.info(`Migrated ${count} chat sessions from user_id='default' to ${realOwnerId}`);
96624
97596
  }
96625
97597
  return count;
96626
97598
  }
@@ -96766,13 +97738,13 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96766
97738
  this.db = db;
96767
97739
  }
96768
97740
  create(data) {
96769
- this.db.prepare("INSERT INTO users (id, org_id, name, email, role, team_id, password_hash, created_at) VALUES (?,?,?,?,?,?,?,?)").run(data.id, data.orgId, data.name, data.email ?? null, data.role ?? "member", data.teamId ?? null, data.passwordHash ?? null, now2());
97741
+ this.db.prepare("INSERT INTO users (id, org_id, name, email, role, team_id, password_hash, hub_user_id, avatar_url, created_at) VALUES (?,?,?,?,?,?,?,?,?,?)").run(data.id, data.orgId, data.name, data.email ?? null, data.role ?? "member", data.teamId ?? null, data.passwordHash ?? null, data.hubUserId ?? null, data.avatarUrl ?? null, now2());
96770
97742
  return this.findById(data.id);
96771
97743
  }
96772
97744
  async upsert(data) {
96773
- this.db.prepare(`INSERT INTO users (id, org_id, name, email, role, team_id, password_hash, created_at)
96774
- VALUES (?,?,?,?,?,?,?,?)
96775
- ON CONFLICT(id) DO UPDATE SET name = excluded.name, email = excluded.email, role = excluded.role, team_id = excluded.team_id, password_hash = COALESCE(excluded.password_hash, password_hash)`).run(data.id, data.orgId, data.name, data.email ?? null, data.role ?? "member", data.teamId ?? null, data.passwordHash ?? null, now2());
97745
+ this.db.prepare(`INSERT INTO users (id, org_id, name, email, role, team_id, password_hash, hub_user_id, created_at)
97746
+ VALUES (?,?,?,?,?,?,?,?,?)
97747
+ ON CONFLICT(id) DO UPDATE SET name = excluded.name, email = excluded.email, role = excluded.role, team_id = excluded.team_id, password_hash = COALESCE(excluded.password_hash, password_hash), hub_user_id = COALESCE(excluded.hub_user_id, hub_user_id)`).run(data.id, data.orgId, data.name, data.email ?? null, data.role ?? "member", data.teamId ?? null, data.passwordHash ?? null, data.hubUserId ?? null, now2());
96776
97748
  }
96777
97749
  async updateTeamId(id, teamId) {
96778
97750
  this.db.prepare("UPDATE users SET team_id = ? WHERE id = ?").run(teamId, id);
@@ -96787,6 +97759,17 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96787
97759
  const r = this.db.prepare("SELECT * FROM users WHERE email = ? AND deleted_at IS NULL").get(email);
96788
97760
  return r ? this._map(r) : null;
96789
97761
  }
97762
+ findByHubUserId(hubUserId) {
97763
+ const r = this.db.prepare("SELECT * FROM users WHERE hub_user_id = ? AND deleted_at IS NULL").get(hubUserId);
97764
+ return r ? this._map(r) : null;
97765
+ }
97766
+ updateHubUserId(id, hubUserId, hubUsername) {
97767
+ if (hubUsername) {
97768
+ this.db.prepare("UPDATE users SET hub_user_id = ?, hub_username = ? WHERE id = ?").run(hubUserId, hubUsername, id);
97769
+ } else {
97770
+ this.db.prepare("UPDATE users SET hub_user_id = ? WHERE id = ?").run(hubUserId, id);
97771
+ }
97772
+ }
96790
97773
  findById(id) {
96791
97774
  const r = this.db.prepare("SELECT * FROM users WHERE id = ?").get(id);
96792
97775
  return r ? this._map(r) : null;
@@ -96863,7 +97846,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96863
97846
  return newId;
96864
97847
  }
96865
97848
  this.db.prepare("UPDATE users SET id = ? WHERE id = 'default'").run(newId);
96866
- log75.info(`Migrated user id='default' to '${newId}'`);
97849
+ log77.info(`Migrated user id='default' to '${newId}'`);
96867
97850
  return newId;
96868
97851
  }
96869
97852
  _map(r) {
@@ -96876,6 +97859,8 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
96876
97859
  teamId: r["team_id"],
96877
97860
  passwordHash: r["password_hash"],
96878
97861
  avatarUrl: r["avatar_url"],
97862
+ hubUserId: r["hub_user_id"],
97863
+ hubUsername: r["hub_username"],
96879
97864
  inviteToken: r["invite_token"],
96880
97865
  inviteExpiresAt: r["invite_expires_at"],
96881
97866
  createdAt: toDate(r["created_at"]),
@@ -97922,7 +98907,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
97922
98907
  const result = this.db.prepare("UPDATE user_notifications SET user_id = ? WHERE user_id = 'default'").run(realOwnerId);
97923
98908
  const count = Number(result.changes);
97924
98909
  if (count > 0) {
97925
- log75.info(`Migrated ${count} notifications from user_id='default' to ${realOwnerId}`);
98910
+ log77.info(`Migrated ${count} notifications from user_id='default' to ${realOwnerId}`);
97926
98911
  }
97927
98912
  return count;
97928
98913
  }
@@ -98003,7 +98988,7 @@ CREATE TABLE IF NOT EXISTS user_read_cursors (
98003
98988
  const result = this.db.prepare("UPDATE approvals SET target_user_id = ? WHERE target_user_id = 'default'").run(realOwnerId);
98004
98989
  const count = Number(result.changes);
98005
98990
  if (count > 0) {
98006
- log75.info(`Migrated ${count} approvals from target_user_id='default' to ${realOwnerId}`);
98991
+ log77.info(`Migrated ${count} approvals from target_user_id='default' to ${realOwnerId}`);
98007
98992
  }
98008
98993
  return count;
98009
98994
  }
@@ -98252,17 +99237,17 @@ var init_dist5 = __esm({
98252
99237
  });
98253
99238
 
98254
99239
  // ../org-manager/dist/storage-bridge.js
98255
- import { homedir as homedir16 } from "node:os";
98256
- import { join as join27 } from "node:path";
99240
+ import { homedir as homedir18 } from "node:os";
99241
+ import { join as join29 } from "node:path";
98257
99242
  function resolveSqlitePath(url) {
98258
99243
  if (url?.startsWith("sqlite:")) {
98259
99244
  let p = url.slice("sqlite:".length);
98260
99245
  if (p.startsWith("~/") || p === "~") {
98261
- p = join27(homedir16(), p.slice(2));
99246
+ p = join29(homedir18(), p.slice(2));
98262
99247
  }
98263
99248
  return p;
98264
99249
  }
98265
- return join27(homedir16(), ".markus", "data.db");
99250
+ return join29(homedir18(), ".markus", "data.db");
98266
99251
  }
98267
99252
  async function initStorage(databaseUrl) {
98268
99253
  const url = databaseUrl ?? process.env["DATABASE_URL"];
@@ -98300,28 +99285,28 @@ async function initSqliteStorage(url) {
98300
99285
  statusTransitionRepo: new storage.SqliteStatusTransitionRepo(db),
98301
99286
  readCursorRepo: new storage.SqliteReadCursorRepo(db)
98302
99287
  };
98303
- log76.info("SQLite storage initialized", { path: dbPath });
99288
+ log78.info("SQLite storage initialized", { path: dbPath });
98304
99289
  return bridge;
98305
99290
  } catch (error) {
98306
- log76.warn("Failed to initialize SQLite storage, falling back to memory-only mode", {
99291
+ log78.warn("Failed to initialize SQLite storage, falling back to memory-only mode", {
98307
99292
  error: String(error)
98308
99293
  });
98309
99294
  return null;
98310
99295
  }
98311
99296
  }
98312
- var log76;
99297
+ var log78;
98313
99298
  var init_storage_bridge = __esm({
98314
99299
  "../org-manager/dist/storage-bridge.js"() {
98315
99300
  "use strict";
98316
99301
  init_dist();
98317
- log76 = createLogger("storage-bridge");
99302
+ log78 = createLogger("storage-bridge");
98318
99303
  }
98319
99304
  });
98320
99305
 
98321
99306
  // ../org-manager/dist/file-storage-provider.js
98322
- import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync19, unlinkSync as unlinkSync4, existsSync as existsSync32 } from "node:fs";
98323
- import { join as join28, extname } from "node:path";
98324
- import { homedir as homedir17 } from "node:os";
99307
+ import { mkdirSync as mkdirSync25, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, existsSync as existsSync34 } from "node:fs";
99308
+ import { join as join30, extname } from "node:path";
99309
+ import { homedir as homedir19 } from "node:os";
98325
99310
  function mimeToExt(mime) {
98326
99311
  const map = {
98327
99312
  "image/jpeg": ".jpg",
@@ -98341,27 +99326,27 @@ var init_file_storage_provider = __esm({
98341
99326
  LocalFileStorageProvider = class {
98342
99327
  baseDir;
98343
99328
  constructor(baseDir) {
98344
- this.baseDir = baseDir ?? join28(homedir17(), ".markus", "uploads");
98345
- mkdirSync23(this.baseDir, { recursive: true });
99329
+ this.baseDir = baseDir ?? join30(homedir19(), ".markus", "uploads");
99330
+ mkdirSync25(this.baseDir, { recursive: true });
98346
99331
  }
98347
99332
  async upload(data, opts) {
98348
99333
  const ext = extname(opts.name) || mimeToExt(opts.contentType);
98349
99334
  const key2 = `${generateId("upl")}${ext}`;
98350
- const subDir = opts.prefix ? join28(this.baseDir, opts.prefix) : this.baseDir;
98351
- mkdirSync23(subDir, { recursive: true });
98352
- writeFileSync19(join28(subDir, key2), data);
99335
+ const subDir = opts.prefix ? join30(this.baseDir, opts.prefix) : this.baseDir;
99336
+ mkdirSync25(subDir, { recursive: true });
99337
+ writeFileSync21(join30(subDir, key2), data);
98353
99338
  const urlPath = opts.prefix ? `/api/uploads/${opts.prefix}/${key2}` : `/api/uploads/${key2}`;
98354
99339
  return { url: urlPath, key: opts.prefix ? `${opts.prefix}/${key2}` : key2 };
98355
99340
  }
98356
99341
  async delete(key2) {
98357
- const filePath = join28(this.baseDir, key2);
98358
- if (existsSync32(filePath)) {
99342
+ const filePath = join30(this.baseDir, key2);
99343
+ if (existsSync34(filePath)) {
98359
99344
  unlinkSync4(filePath);
98360
99345
  }
98361
99346
  }
98362
99347
  /** Resolve a storage key to an absolute filesystem path (for serving). */
98363
99348
  resolve(key2) {
98364
- return join28(this.baseDir, key2);
99349
+ return join30(this.baseDir, key2);
98365
99350
  }
98366
99351
  };
98367
99352
  }
@@ -98379,6 +99364,7 @@ __export(dist_exports5, {
98379
99364
  FileKnowledgeStore: () => FileKnowledgeStore,
98380
99365
  HITLService: () => HITLService,
98381
99366
  KnowledgeService: () => KnowledgeService,
99367
+ LicenseService: () => LicenseService,
98382
99368
  LocalFileStorageProvider: () => LocalFileStorageProvider,
98383
99369
  OrganizationService: () => OrganizationService,
98384
99370
  ProjectService: () => ProjectService,
@@ -98387,6 +99373,7 @@ __export(dist_exports5, {
98387
99373
  ScheduledTaskRunner: () => ScheduledTaskRunner,
98388
99374
  StaleDetector: () => StaleDetector,
98389
99375
  TaskService: () => TaskService,
99376
+ TelemetryService: () => TelemetryService,
98390
99377
  TrustService: () => TrustService,
98391
99378
  WSBroadcaster: () => WSBroadcaster,
98392
99379
  initStorage: () => initStorage,
@@ -98402,6 +99389,8 @@ var init_dist6 = __esm({
98402
99389
  init_ws_server();
98403
99390
  init_hitl_service();
98404
99391
  init_billing_service();
99392
+ init_license_service();
99393
+ init_telemetry_service();
98405
99394
  init_audit_service();
98406
99395
  init_project_service();
98407
99396
  init_requirement_service();
@@ -98421,12 +99410,12 @@ var init_dist6 = __esm({
98421
99410
  });
98422
99411
 
98423
99412
  // ../comms/dist/feishu/client.js
98424
- var log77, FeishuClient;
99413
+ var log79, FeishuClient;
98425
99414
  var init_client = __esm({
98426
99415
  "../comms/dist/feishu/client.js"() {
98427
99416
  "use strict";
98428
99417
  init_dist();
98429
- log77 = createLogger("feishu-client");
99418
+ log79 = createLogger("feishu-client");
98430
99419
  FeishuClient = class {
98431
99420
  appId;
98432
99421
  appSecret;
@@ -98456,7 +99445,7 @@ var init_client = __esm({
98456
99445
  }
98457
99446
  this.tenantToken = data.tenant_access_token;
98458
99447
  this.tokenExpiresAt = Date.now() + (data.expire - 300) * 1e3;
98459
- log77.info("Feishu tenant token refreshed");
99448
+ log79.info("Feishu tenant token refreshed");
98460
99449
  return this.tenantToken;
98461
99450
  }
98462
99451
  async sendTextMessage(chatId, text) {
@@ -98590,13 +99579,13 @@ var init_client = __esm({
98590
99579
  import { createServer as createServer3 } from "node:http";
98591
99580
  import { createDecipheriv, scrypt } from "node:crypto";
98592
99581
  import { promisify as promisify3 } from "node:util";
98593
- var log78, scryptAsync, FeishuAdapter;
99582
+ var log80, scryptAsync, FeishuAdapter;
98594
99583
  var init_adapter = __esm({
98595
99584
  "../comms/dist/feishu/adapter.js"() {
98596
99585
  "use strict";
98597
99586
  init_dist();
98598
99587
  init_client();
98599
- log78 = createLogger("feishu-adapter");
99588
+ log80 = createLogger("feishu-adapter");
98600
99589
  scryptAsync = promisify3(scrypt);
98601
99590
  FeishuAdapter = class {
98602
99591
  platform = "feishu";
@@ -98617,10 +99606,10 @@ var init_adapter = __esm({
98617
99606
  const port = this.config.webhookPort ?? 9e3;
98618
99607
  this.server = createServer3((req, res) => this.handleWebhook(req, res));
98619
99608
  this.server.listen(port, () => {
98620
- log78.info(`Feishu webhook server listening on port ${port}`);
99609
+ log80.info(`Feishu webhook server listening on port ${port}`);
98621
99610
  });
98622
99611
  this.connected = true;
98623
- log78.info("Feishu adapter connected");
99612
+ log80.info("Feishu adapter connected");
98624
99613
  }
98625
99614
  async disconnect() {
98626
99615
  if (this.server) {
@@ -98628,7 +99617,7 @@ var init_adapter = __esm({
98628
99617
  this.server = void 0;
98629
99618
  }
98630
99619
  this.connected = false;
98631
- log78.info("Feishu adapter disconnected");
99620
+ log80.info("Feishu adapter disconnected");
98632
99621
  }
98633
99622
  async sendMessage(channelId, content, options) {
98634
99623
  if (!this.client)
@@ -98704,12 +99693,12 @@ var init_adapter = __esm({
98704
99693
  res.end("ok");
98705
99694
  if (event.header?.event_type === "im.message.receive_v1") {
98706
99695
  this.processMessageEvent(event).catch((err) => {
98707
- log78.error("Failed to process Feishu message event", { error: err.message });
99696
+ log80.error("Failed to process Feishu message event", { error: err.message });
98708
99697
  });
98709
99698
  }
98710
99699
  if (event["action"]) {
98711
99700
  this.processCardAction(event).catch((err) => {
98712
- log78.error("Failed to process card action", { error: err.message });
99701
+ log80.error("Failed to process card action", { error: err.message });
98713
99702
  });
98714
99703
  }
98715
99704
  };
@@ -98720,14 +99709,14 @@ var init_adapter = __esm({
98720
99709
  const event = JSON.parse(decrypted);
98721
99710
  processEvent(event);
98722
99711
  } else if (raw.encrypt && !this.config?.encryptKey) {
98723
- log78.warn("Received encrypted Feishu payload but no encryptKey configured");
99712
+ log80.warn("Received encrypted Feishu payload but no encryptKey configured");
98724
99713
  res.writeHead(200);
98725
99714
  res.end("ok");
98726
99715
  } else {
98727
99716
  processEvent(raw);
98728
99717
  }
98729
99718
  } catch (err) {
98730
- log78.error("Failed to process Feishu webhook", { error: err instanceof Error ? err.message : String(err) });
99719
+ log80.error("Failed to process Feishu webhook", { error: err instanceof Error ? err.message : String(err) });
98731
99720
  res.writeHead(400);
98732
99721
  res.end("bad request");
98733
99722
  }
@@ -98772,7 +99761,7 @@ var init_adapter = __esm({
98772
99761
  try {
98773
99762
  await handler4(message);
98774
99763
  } catch (error) {
98775
- log78.error("Message handler failed", { error });
99764
+ log80.error("Message handler failed", { error });
98776
99765
  }
98777
99766
  }
98778
99767
  }
@@ -98807,7 +99796,7 @@ var init_adapter = __esm({
98807
99796
  try {
98808
99797
  await handler4(message);
98809
99798
  } catch (error) {
98810
- log78.error("Card action handler failed", { error });
99799
+ log80.error("Card action handler failed", { error });
98811
99800
  }
98812
99801
  }
98813
99802
  }
@@ -98824,12 +99813,12 @@ var init_cards = __esm({
98824
99813
 
98825
99814
  // ../comms/dist/webui/adapter.js
98826
99815
  import { createServer as createServer4 } from "node:http";
98827
- var log79, WebUIAdapter;
99816
+ var log81, WebUIAdapter;
98828
99817
  var init_adapter2 = __esm({
98829
99818
  "../comms/dist/webui/adapter.js"() {
98830
99819
  "use strict";
98831
99820
  init_dist();
98832
- log79 = createLogger("webui-adapter");
99821
+ log81 = createLogger("webui-adapter");
98833
99822
  WebUIAdapter = class {
98834
99823
  platform = "webui";
98835
99824
  handlers = [];
@@ -98841,7 +99830,7 @@ var init_adapter2 = __esm({
98841
99830
  this.port = config["port"] ?? 8058;
98842
99831
  this.server = createServer4((req, res) => this.handleRequest(req, res));
98843
99832
  this.server.listen(this.port, "0.0.0.0", () => {
98844
- log79.info(`WebUI comm server listening on 0.0.0.0:${this.port}`);
99833
+ log81.info(`WebUI comm server listening on 0.0.0.0:${this.port}`);
98845
99834
  });
98846
99835
  this.connected = true;
98847
99836
  }
@@ -98914,7 +99903,7 @@ var init_adapter2 = __esm({
98914
99903
  res.end(JSON.stringify({ received: true, messageId: message.id }));
98915
99904
  for (const handler4 of this.handlers) {
98916
99905
  handler4(message).catch((err) => {
98917
- log79.error("WebUI message handler failed", { error: String(err) });
99906
+ log81.error("WebUI message handler failed", { error: String(err) });
98918
99907
  });
98919
99908
  }
98920
99909
  } catch (error) {
@@ -98928,87 +99917,87 @@ var init_adapter2 = __esm({
98928
99917
  });
98929
99918
 
98930
99919
  // ../comms/dist/whatsapp/client.js
98931
- var log80;
99920
+ var log82;
98932
99921
  var init_client2 = __esm({
98933
99922
  "../comms/dist/whatsapp/client.js"() {
98934
99923
  "use strict";
98935
99924
  init_dist();
98936
- log80 = createLogger("whatsapp-client");
99925
+ log82 = createLogger("whatsapp-client");
98937
99926
  }
98938
99927
  });
98939
99928
 
98940
99929
  // ../comms/dist/whatsapp/adapter.js
98941
- var log81;
99930
+ var log83;
98942
99931
  var init_adapter3 = __esm({
98943
99932
  "../comms/dist/whatsapp/adapter.js"() {
98944
99933
  "use strict";
98945
99934
  init_dist();
98946
99935
  init_client2();
98947
- log81 = createLogger("whatsapp-adapter");
99936
+ log83 = createLogger("whatsapp-adapter");
98948
99937
  }
98949
99938
  });
98950
99939
 
98951
99940
  // ../comms/dist/slack/client.js
98952
- var log82;
99941
+ var log84;
98953
99942
  var init_client3 = __esm({
98954
99943
  "../comms/dist/slack/client.js"() {
98955
99944
  "use strict";
98956
99945
  init_dist();
98957
- log82 = createLogger("slack-client");
99946
+ log84 = createLogger("slack-client");
98958
99947
  }
98959
99948
  });
98960
99949
 
98961
99950
  // ../comms/dist/slack/adapter.js
98962
- var log83;
99951
+ var log85;
98963
99952
  var init_adapter4 = __esm({
98964
99953
  "../comms/dist/slack/adapter.js"() {
98965
99954
  "use strict";
98966
99955
  init_dist();
98967
99956
  init_client3();
98968
- log83 = createLogger("slack-adapter");
99957
+ log85 = createLogger("slack-adapter");
98969
99958
  }
98970
99959
  });
98971
99960
 
98972
99961
  // ../comms/dist/telegram/client.js
98973
- var log84;
99962
+ var log86;
98974
99963
  var init_client4 = __esm({
98975
99964
  "../comms/dist/telegram/client.js"() {
98976
99965
  "use strict";
98977
99966
  init_dist();
98978
- log84 = createLogger("telegram-client");
99967
+ log86 = createLogger("telegram-client");
98979
99968
  }
98980
99969
  });
98981
99970
 
98982
99971
  // ../comms/dist/telegram/adapter.js
98983
- var log85;
99972
+ var log87;
98984
99973
  var init_adapter5 = __esm({
98985
99974
  "../comms/dist/telegram/adapter.js"() {
98986
99975
  "use strict";
98987
99976
  init_dist();
98988
99977
  init_client4();
98989
- log85 = createLogger("telegram-adapter");
99978
+ log87 = createLogger("telegram-adapter");
98990
99979
  }
98991
99980
  });
98992
99981
 
98993
99982
  // ../comms/dist/router.js
98994
- var log86, MessageRouter;
99983
+ var log88, MessageRouter;
98995
99984
  var init_router2 = __esm({
98996
99985
  "../comms/dist/router.js"() {
98997
99986
  "use strict";
98998
99987
  init_dist();
98999
- log86 = createLogger("message-router");
99988
+ log88 = createLogger("message-router");
99000
99989
  MessageRouter = class {
99001
99990
  adapters = /* @__PURE__ */ new Map();
99002
99991
  agentChannelMap = /* @__PURE__ */ new Map();
99003
99992
  agentHandler;
99004
99993
  registerAdapter(adapter2) {
99005
99994
  this.adapters.set(adapter2.platform, adapter2);
99006
- log86.info(`Registered comm adapter: ${adapter2.platform}`);
99995
+ log88.info(`Registered comm adapter: ${adapter2.platform}`);
99007
99996
  }
99008
- bindAgentToChannel(agentId2, platform4, channelId) {
99009
- const key2 = `${platform4}:${channelId}`;
99997
+ bindAgentToChannel(agentId2, platform5, channelId) {
99998
+ const key2 = `${platform5}:${channelId}`;
99010
99999
  this.agentChannelMap.set(key2, agentId2);
99011
- log86.info(`Bound agent ${agentId2} to ${key2}`);
100000
+ log88.info(`Bound agent ${agentId2} to ${key2}`);
99012
100001
  }
99013
100002
  setAgentHandler(handler4) {
99014
100003
  this.agentHandler = handler4;
@@ -99017,7 +100006,7 @@ var init_router2 = __esm({
99017
100006
  for (const config of configs) {
99018
100007
  const adapter2 = this.adapters.get(config.platform);
99019
100008
  if (!adapter2) {
99020
- log86.warn(`No adapter registered for platform: ${config.platform}`);
100009
+ log88.warn(`No adapter registered for platform: ${config.platform}`);
99021
100010
  continue;
99022
100011
  }
99023
100012
  await adapter2.connect(config);
@@ -99033,22 +100022,22 @@ var init_router2 = __esm({
99033
100022
  }
99034
100023
  }
99035
100024
  }
99036
- async sendToChannel(platform4, channelId, content) {
99037
- const adapter2 = this.adapters.get(platform4);
100025
+ async sendToChannel(platform5, channelId, content) {
100026
+ const adapter2 = this.adapters.get(platform5);
99038
100027
  if (!adapter2 || !adapter2.isConnected()) {
99039
- log86.warn(`Adapter not available for platform: ${platform4}`);
100028
+ log88.warn(`Adapter not available for platform: ${platform5}`);
99040
100029
  return void 0;
99041
100030
  }
99042
100031
  return adapter2.sendMessage(channelId, content);
99043
100032
  }
99044
- async sendAsAgent(agentId2, platform4, channelId, content) {
99045
- return this.sendToChannel(platform4, channelId, content);
100033
+ async sendAsAgent(agentId2, platform5, channelId, content) {
100034
+ return this.sendToChannel(platform5, channelId, content);
99046
100035
  }
99047
100036
  async routeIncomingMessage(message) {
99048
100037
  const key2 = `${message.platform}:${message.channelId}`;
99049
100038
  const agentId2 = message.agentId || this.agentChannelMap.get(key2);
99050
100039
  if (!agentId2) {
99051
- log86.debug("No agent bound to channel, skipping message", { key: key2 });
100040
+ log88.debug("No agent bound to channel, skipping message", { key: key2 });
99052
100041
  return;
99053
100042
  }
99054
100043
  message.agentId = agentId2;
@@ -99064,7 +100053,7 @@ var init_router2 = __esm({
99064
100053
  }
99065
100054
  }
99066
100055
  } catch (error) {
99067
- log86.error("Agent handler failed", { agentId: agentId2, error: String(error) });
100056
+ log88.error("Agent handler failed", { agentId: agentId2, error: String(error) });
99068
100057
  }
99069
100058
  }
99070
100059
  }
@@ -99091,17 +100080,17 @@ var init_dist7 = __esm({
99091
100080
  });
99092
100081
 
99093
100082
  // src/utils/logger.ts
99094
- import { createWriteStream as createWriteStream2, existsSync as existsSync33, mkdirSync as mkdirSync24, appendFileSync as appendFileSync3 } from "node:fs";
99095
- import { join as join29 } from "node:path";
99096
- import { homedir as homedir18 } from "node:os";
100083
+ import { createWriteStream as createWriteStream2, existsSync as existsSync35, mkdirSync as mkdirSync26, appendFileSync as appendFileSync3 } from "node:fs";
100084
+ import { join as join31 } from "node:path";
100085
+ import { homedir as homedir20 } from "node:os";
99097
100086
  function ensureLogDir2() {
99098
- if (!existsSync33(LOG_DIR2)) {
99099
- mkdirSync24(LOG_DIR2, { recursive: true, mode: 493 });
100087
+ if (!existsSync35(LOG_DIR2)) {
100088
+ mkdirSync26(LOG_DIR2, { recursive: true, mode: 493 });
99100
100089
  }
99101
100090
  }
99102
100091
  function getStartupLogPath() {
99103
100092
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
99104
- return join29(LOG_DIR2, `startup-${date}.log`);
100093
+ return join31(LOG_DIR2, `startup-${date}.log`);
99105
100094
  }
99106
100095
  function setSuppressConsole(suppress) {
99107
100096
  _suppressConsole = suppress;
@@ -99156,7 +100145,7 @@ var LOG_DIR2, startupLogStream, startupLogPath, _suppressConsole, LEVEL_PREFIX;
99156
100145
  var init_logger2 = __esm({
99157
100146
  "src/utils/logger.ts"() {
99158
100147
  "use strict";
99159
- LOG_DIR2 = join29(homedir18(), ".markus", "logs");
100148
+ LOG_DIR2 = join31(homedir20(), ".markus", "logs");
99160
100149
  startupLogStream = null;
99161
100150
  startupLogPath = "";
99162
100151
  _suppressConsole = false;
@@ -99174,10 +100163,10 @@ var init_logger2 = __esm({
99174
100163
  // src/utils/browser.ts
99175
100164
  import { exec } from "node:child_process";
99176
100165
  import { get as httpGet } from "node:http";
99177
- import { platform as platform3 } from "node:os";
100166
+ import { platform as platform4 } from "node:os";
99178
100167
  function openBrowser(url) {
99179
100168
  if (process.env["NO_BROWSER"]) return;
99180
- const sys = platform3();
100169
+ const sys = platform4();
99181
100170
  const cmd = sys === "darwin" ? `open "${url}"` : sys === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
99182
100171
  exec(cmd, (err) => {
99183
100172
  if (err) {
@@ -99212,9 +100201,9 @@ var init_browser = __esm({
99212
100201
  });
99213
100202
 
99214
100203
  // src/utils/startupProgress.ts
99215
- import { homedir as homedir19 } from "node:os";
99216
- import { appendFileSync as appendFileSync4, existsSync as existsSync34, mkdirSync as mkdirSync25 } from "node:fs";
99217
- import { join as join30 } from "node:path";
100204
+ import { homedir as homedir21 } from "node:os";
100205
+ import { appendFileSync as appendFileSync4, existsSync as existsSync36, mkdirSync as mkdirSync27 } from "node:fs";
100206
+ import { join as join32 } from "node:path";
99218
100207
  function clearScreen() {
99219
100208
  return "\x1B[2J\x1B[H";
99220
100209
  }
@@ -99376,8 +100365,8 @@ var init_startupProgress = __esm({
99376
100365
  const line = `${ts} ${msg}
99377
100366
  `;
99378
100367
  try {
99379
- const dir = join30(homedir19(), ".markus", "logs");
99380
- if (!existsSync34(dir)) mkdirSync25(dir, { recursive: true, mode: 493 });
100368
+ const dir = join32(homedir21(), ".markus", "logs");
100369
+ if (!existsSync36(dir)) mkdirSync27(dir, { recursive: true, mode: 493 });
99381
100370
  appendFileSync4(this.logPath, line, { mode: 420 });
99382
100371
  } catch {
99383
100372
  }
@@ -99486,13 +100475,13 @@ var init_startupProgress = __esm({
99486
100475
  });
99487
100476
 
99488
100477
  // src/connector-service.ts
99489
- import { resolve as resolve16, join as join31, dirname as dirname10 } from "node:path";
99490
- import { existsSync as existsSync35, readFileSync as readFileSync24, writeFileSync as writeFileSync20, mkdirSync as mkdirSync26, readdirSync as readdirSync13, cpSync as cpSync4 } from "node:fs";
99491
- import { homedir as homedir20 } from "node:os";
100478
+ import { resolve as resolve16, join as join33, dirname as dirname12 } from "node:path";
100479
+ import { existsSync as existsSync37, readFileSync as readFileSync26, writeFileSync as writeFileSync22, mkdirSync as mkdirSync28, readdirSync as readdirSync13, cpSync as cpSync4 } from "node:fs";
100480
+ import { homedir as homedir22 } from "node:os";
99492
100481
  import { execSync as execSync4 } from "node:child_process";
99493
100482
  import { fileURLToPath as fileURLToPath6 } from "node:url";
99494
100483
  function expandHome(p) {
99495
- return p.replace(/^~/, homedir20());
100484
+ return p.replace(/^~/, homedir22());
99496
100485
  }
99497
100486
  function loadConnectors() {
99498
100487
  const connectors = /* @__PURE__ */ new Map();
@@ -99500,16 +100489,16 @@ function loadConnectors() {
99500
100489
  loadFromDir(builtinDir, connectors);
99501
100490
  const devDir = resolve16(process.cwd(), "packages", "cli", "connectors");
99502
100491
  if (devDir !== builtinDir) loadFromDir(devDir, connectors);
99503
- const userDir = join31(homedir20(), ".markus", "connectors");
100492
+ const userDir = join33(homedir22(), ".markus", "connectors");
99504
100493
  loadFromDir(userDir, connectors);
99505
100494
  return [...connectors.values()].filter((c) => c.platform !== "_template");
99506
100495
  }
99507
100496
  function loadFromDir(dir, map) {
99508
- if (!existsSync35(dir)) return;
100497
+ if (!existsSync37(dir)) return;
99509
100498
  for (const file of readdirSync13(dir)) {
99510
100499
  if (!file.endsWith(".json") || file.startsWith("_")) continue;
99511
100500
  try {
99512
- const raw = readFileSync24(join31(dir, file), "utf-8");
100501
+ const raw = readFileSync26(join33(dir, file), "utf-8");
99513
100502
  const desc = JSON.parse(raw);
99514
100503
  if (desc.platform) {
99515
100504
  map.set(desc.platform, desc);
@@ -99518,8 +100507,8 @@ function loadFromDir(dir, map) {
99518
100507
  }
99519
100508
  }
99520
100509
  }
99521
- function findConnector(platform4) {
99522
- return loadConnectors().find((c) => c.platform === platform4);
100510
+ function findConnector(platform5) {
100511
+ return loadConnectors().find((c) => c.platform === platform5);
99523
100512
  }
99524
100513
  function scanInstalledPlatforms() {
99525
100514
  const connectors = loadConnectors();
@@ -99534,7 +100523,7 @@ function scanInstalledPlatforms() {
99534
100523
  };
99535
100524
  for (const p of c.detection.configPaths) {
99536
100525
  const expanded = expandHome(p);
99537
- if (existsSync35(expanded)) {
100526
+ if (existsSync37(expanded)) {
99538
100527
  result.installed = true;
99539
100528
  result.configPath = expanded;
99540
100529
  break;
@@ -99561,9 +100550,9 @@ function scanInstalledPlatforms() {
99561
100550
  }
99562
100551
  function readPlatformConfig(connector) {
99563
100552
  const configPath = expandHome(connector.integration.configPath);
99564
- if (!existsSync35(configPath)) return null;
100553
+ if (!existsSync37(configPath)) return null;
99565
100554
  try {
99566
- const raw = readFileSync24(configPath, "utf-8");
100555
+ const raw = readFileSync26(configPath, "utf-8");
99567
100556
  if (connector.integration.configFormat === "json5") {
99568
100557
  const cleaned = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([\]}])/g, "$1");
99569
100558
  return JSON.parse(cleaned);
@@ -99575,12 +100564,12 @@ function readPlatformConfig(connector) {
99575
100564
  }
99576
100565
  function writePlatformConfig(connector, markusUrl, token) {
99577
100566
  const configPath = expandHome(connector.integration.configPath);
99578
- const configDir = dirname10(configPath);
99579
- if (!existsSync35(configDir)) {
99580
- mkdirSync26(configDir, { recursive: true });
100567
+ const configDir = dirname12(configPath);
100568
+ if (!existsSync37(configDir)) {
100569
+ mkdirSync28(configDir, { recursive: true });
99581
100570
  }
99582
100571
  let config = {};
99583
- if (existsSync35(configPath)) {
100572
+ if (existsSync37(configPath)) {
99584
100573
  const existing = readPlatformConfig(connector);
99585
100574
  if (existing) config = existing;
99586
100575
  }
@@ -99588,7 +100577,7 @@ function writePlatformConfig(connector, markusUrl, token) {
99588
100577
  setNestedField(config, connector.integration.tokenField, token);
99589
100578
  try {
99590
100579
  const content = JSON.stringify(config, null, 2);
99591
- writeFileSync20(configPath, content, "utf-8");
100580
+ writeFileSync22(configPath, content, "utf-8");
99592
100581
  return true;
99593
100582
  } catch {
99594
100583
  return false;
@@ -99601,21 +100590,21 @@ function installSkillTemplate(connector) {
99601
100590
  const skillDir = expandHome(connector.integration.skillDir);
99602
100591
  const templateName = connector.integration.skillTemplateName;
99603
100592
  const candidates = [
99604
- join31(homedir20(), ".markus", "templates", templateName),
100593
+ join33(homedir22(), ".markus", "templates", templateName),
99605
100594
  resolve16(process.cwd(), "templates", templateName),
99606
100595
  resolve16(__dirname5, "..", "templates", templateName)
99607
100596
  ];
99608
100597
  let sourceDir;
99609
100598
  for (const c of candidates) {
99610
- if (existsSync35(c)) {
100599
+ if (existsSync37(c)) {
99611
100600
  sourceDir = c;
99612
100601
  break;
99613
100602
  }
99614
100603
  }
99615
100604
  if (!sourceDir) return false;
99616
- const targetDir = join31(skillDir, templateName);
99617
- if (!existsSync35(targetDir)) {
99618
- mkdirSync26(targetDir, { recursive: true });
100605
+ const targetDir = join33(skillDir, templateName);
100606
+ if (!existsSync37(targetDir)) {
100607
+ mkdirSync28(targetDir, { recursive: true });
99619
100608
  }
99620
100609
  try {
99621
100610
  cpSync4(sourceDir, targetDir, { recursive: true });
@@ -99657,7 +100646,7 @@ var init_connector_service = __esm({
99657
100646
  "src/connector-service.ts"() {
99658
100647
  "use strict";
99659
100648
  __filename4 = fileURLToPath6(import.meta.url);
99660
- __dirname5 = dirname10(__filename4);
100649
+ __dirname5 = dirname12(__filename4);
99661
100650
  }
99662
100651
  });
99663
100652
 
@@ -99668,8 +100657,8 @@ __export(init_exports, {
99668
100657
  registerInitCommand: () => registerInitCommand
99669
100658
  });
99670
100659
  import { resolve as resolve17 } from "node:path";
99671
- import { readFileSync as readFileSync25, existsSync as existsSync36, cpSync as cpSync5 } from "node:fs";
99672
- import { homedir as homedir21 } from "node:os";
100660
+ import { readFileSync as readFileSync27, existsSync as existsSync38, cpSync as cpSync5 } from "node:fs";
100661
+ import { homedir as homedir23 } from "node:os";
99673
100662
  function registerInitCommand(program2) {
99674
100663
  program2.command("init").description("Setup wizard: configure LLM provider, API keys, and server settings").option("--force", "Overwrite existing configuration").option("--non-interactive", "Run without prompts (use env vars or --import-from)").option("--provider <name>", "LLM provider (anthropic/openai/google/minimax/siliconflow/zai/deepseek/ollama)").option("--api-key <key>", "LLM API key").option("--port <port>", "API server port", "8056").option("--import-from <platform>", "Import LLM config from an installed agent platform (e.g. openclaw, hermes)").option("--auto-connect", "Auto-connect detected agent platforms after init").action(async (opts) => {
99675
100664
  await quickInit({
@@ -99684,7 +100673,7 @@ function registerInitCommand(program2) {
99684
100673
  });
99685
100674
  }
99686
100675
  async function quickInit(options) {
99687
- const { writeFileSync: writeFileSync21, mkdirSync: mkdirSync27 } = await import("node:fs");
100676
+ const { writeFileSync: writeFileSync23, mkdirSync: mkdirSync29 } = await import("node:fs");
99688
100677
  const { join: pathJoin } = await import("node:path");
99689
100678
  const readline3 = await import("node:readline");
99690
100679
  const nonInteractive = options?.nonInteractive ?? false;
@@ -99705,7 +100694,7 @@ async function quickInit(options) {
99705
100694
  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
99706
100695
  `);
99707
100696
  const configPath = getDefaultConfigPath();
99708
- if (existsSync36(configPath) && !options?.force) {
100697
+ if (existsSync38(configPath) && !options?.force) {
99709
100698
  if (nonInteractive) {
99710
100699
  console.log(` Existing configuration found. Use --force to overwrite.`);
99711
100700
  rl?.close();
@@ -99738,11 +100727,11 @@ async function quickInit(options) {
99738
100727
  const installedPlatforms = scanInstalledPlatforms().filter((p) => p.installed);
99739
100728
  let openclawPath = "";
99740
100729
  const openclawCandidates = [
99741
- pathJoin(homedir21(), ".openclaw", "openclaw.json"),
99742
- pathJoin(homedir21(), ".openclaw", "openclaw.json5")
100730
+ pathJoin(homedir23(), ".openclaw", "openclaw.json"),
100731
+ pathJoin(homedir23(), ".openclaw", "openclaw.json5")
99743
100732
  ];
99744
100733
  for (const p of openclawCandidates) {
99745
- if (existsSync36(p)) {
100734
+ if (existsSync38(p)) {
99746
100735
  openclawPath = p;
99747
100736
  break;
99748
100737
  }
@@ -99816,7 +100805,7 @@ async function quickInit(options) {
99816
100805
  }
99817
100806
  } else if (mode === "openclaw") {
99818
100807
  try {
99819
- const raw = readFileSync25(openclawPath, "utf-8");
100808
+ const raw = readFileSync27(openclawPath, "utf-8");
99820
100809
  const cleaned = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "").replace(/,\s*([\]}])/g, "$1");
99821
100810
  const parsed = JSON.parse(cleaned);
99822
100811
  const modelsSection = parsed.models;
@@ -99944,18 +100933,18 @@ async function quickInit(options) {
99944
100933
  console.error(`
99945
100934
  Failed to save config: ${e}`);
99946
100935
  }
99947
- const userTemplatesDir = pathJoin(homedir21(), ".markus", "templates");
100936
+ const userTemplatesDir = pathJoin(homedir23(), ".markus", "templates");
99948
100937
  const builtinTemplatesDir = resolveTemplatesDir("roles");
99949
- if (builtinTemplatesDir && existsSync36(builtinTemplatesDir) && !existsSync36(userTemplatesDir)) {
100938
+ if (builtinTemplatesDir && existsSync38(builtinTemplatesDir) && !existsSync38(userTemplatesDir)) {
99950
100939
  const builtinRoot = resolve17(builtinTemplatesDir, "..");
99951
- mkdirSync27(userTemplatesDir, { recursive: true });
100940
+ mkdirSync29(userTemplatesDir, { recursive: true });
99952
100941
  cpSync5(builtinRoot, userTemplatesDir, { recursive: true });
99953
100942
  console.log(` Copied templates to ${userTemplatesDir}`);
99954
100943
  }
99955
100944
  const devRoleDir = pathJoin(userTemplatesDir || pathJoin(process.cwd(), "templates"), "roles", "developer");
99956
- if (!existsSync36(devRoleDir)) {
99957
- mkdirSync27(devRoleDir, { recursive: true });
99958
- writeFileSync21(
100945
+ if (!existsSync38(devRoleDir)) {
100946
+ mkdirSync29(devRoleDir, { recursive: true });
100947
+ writeFileSync23(
99959
100948
  pathJoin(devRoleDir, "ROLE.md"),
99960
100949
  [
99961
100950
  "---",
@@ -99997,7 +100986,7 @@ async function quickInit(options) {
99997
100986
  console.log("");
99998
100987
  }
99999
100988
  console.log(` Config: ${configPath}`);
100000
- console.log(` Data: ${pathJoin(homedir21(), ".markus")}`);
100989
+ console.log(` Data: ${pathJoin(homedir23(), ".markus")}`);
100001
100990
  console.log(` Server: http://localhost:${apiPort}`);
100002
100991
  console.log("");
100003
100992
  }
@@ -100035,12 +101024,12 @@ function signJwt(payload, secret) {
100035
101024
  const sig = base64url(createHmac2("sha256", secret).update(`${header}.${body}`).digest());
100036
101025
  return `${header}.${body}.${sig}`;
100037
101026
  }
100038
- var log87, _rtcModule, STUN_SERVERS, RECONNECT_BASE_MS, RECONNECT_MAX_MS, HEARTBEAT_INTERVAL_MS, PEER_PING_INTERVAL_MS, PEER_PING_TIMEOUT_MS, RELAY_INACTIVITY_TIMEOUT_MS, RemoteAccessAgent;
101027
+ var log89, _rtcModule, STUN_SERVERS, RECONNECT_BASE_MS, RECONNECT_MAX_MS, HEARTBEAT_INTERVAL_MS2, PEER_PING_INTERVAL_MS, PEER_PING_TIMEOUT_MS, RELAY_INACTIVITY_TIMEOUT_MS, RemoteAccessAgent;
100039
101028
  var init_agent3 = __esm({
100040
101029
  "../remote/dist/agent.js"() {
100041
101030
  "use strict";
100042
101031
  init_dist();
100043
- log87 = createLogger("remote");
101032
+ log89 = createLogger("remote");
100044
101033
  _rtcModule = null;
100045
101034
  STUN_SERVERS = [
100046
101035
  "stun:stun.l.google.com:19302",
@@ -100048,7 +101037,7 @@ var init_agent3 = __esm({
100048
101037
  ];
100049
101038
  RECONNECT_BASE_MS = 2e3;
100050
101039
  RECONNECT_MAX_MS = 6e4;
100051
- HEARTBEAT_INTERVAL_MS = 25e3;
101040
+ HEARTBEAT_INTERVAL_MS2 = 25e3;
100052
101041
  PEER_PING_INTERVAL_MS = 15e3;
100053
101042
  PEER_PING_TIMEOUT_MS = 1e4;
100054
101043
  RELAY_INACTIVITY_TIMEOUT_MS = 5 * 6e4;
@@ -100069,19 +101058,19 @@ var init_agent3 = __esm({
100069
101058
  // ── Public API ────────────────────────────────────────────────────────────
100070
101059
  async start() {
100071
101060
  this.destroyed = false;
100072
- log87.info("Starting remote access agent...");
101061
+ log89.info("Starting remote access agent...");
100073
101062
  const rtc = await loadRtcModule();
100074
101063
  rtc.initLogger("Warning");
100075
101064
  await this.discoverLocalOwner();
100076
101065
  try {
100077
101066
  this.registration = await this.registerInstance();
100078
- log87.info("Registered with Hub", {
101067
+ log89.info("Registered with Hub", {
100079
101068
  instanceId: this.registration.instanceId,
100080
101069
  remoteUrl: this.registration.remoteUrl
100081
101070
  });
100082
101071
  this.connectSignaling();
100083
101072
  } catch (err) {
100084
- log87.error("Failed to register with Hub", { error: String(err) });
101073
+ log89.error("Failed to register with Hub", { error: String(err) });
100085
101074
  this.scheduleReconnect();
100086
101075
  }
100087
101076
  }
@@ -100102,14 +101091,14 @@ var init_agent3 = __esm({
100102
101091
  if (users?.length) {
100103
101092
  const owner = users.find((u) => u.role === "owner") ?? users[0];
100104
101093
  this.localOwnerUserId = owner.id;
100105
- log87.info("Discovered local owner", { userId: this.localOwnerUserId });
101094
+ log89.info("Discovered local owner", { userId: this.localOwnerUserId });
100106
101095
  }
100107
101096
  return;
100108
101097
  } catch (err) {
100109
101098
  if (attempt < 2) {
100110
101099
  await new Promise((r) => setTimeout(r, 1e3 * (attempt + 1)));
100111
101100
  } else {
100112
- log87.warn("Failed to discover local owner, using synthetic user", { error: String(err) });
101101
+ log89.warn("Failed to discover local owner, using synthetic user", { error: String(err) });
100113
101102
  }
100114
101103
  }
100115
101104
  }
@@ -100141,7 +101130,7 @@ var init_agent3 = __esm({
100141
101130
  this.registration = null;
100142
101131
  }
100143
101132
  this.emitStatus();
100144
- log87.info("Remote access agent stopped");
101133
+ log89.info("Remote access agent stopped");
100145
101134
  }
100146
101135
  getStatus() {
100147
101136
  const wsOpen = this.ws?.readyState === WebSocket2.OPEN;
@@ -100250,11 +101239,11 @@ var init_agent3 = __esm({
100250
101239
  return;
100251
101240
  const { signalUrl, signalingToken } = this.registration;
100252
101241
  const wsUrl = `${signalUrl}?token=${encodeURIComponent(signalingToken)}`;
100253
- log87.info("Connecting to signal server...", { signalUrl });
101242
+ log89.info("Connecting to signal server...", { signalUrl });
100254
101243
  const ws = new WebSocket2(wsUrl);
100255
101244
  this.ws = ws;
100256
101245
  ws.on("open", () => {
100257
- log87.info("Signal server connected");
101246
+ log89.info("Signal server connected");
100258
101247
  this.reconnectAttempts = 0;
100259
101248
  this.startHeartbeat();
100260
101249
  this.emitStatus();
@@ -100265,11 +101254,11 @@ var init_agent3 = __esm({
100265
101254
  const msg = JSON.parse(data.toString());
100266
101255
  this.handleSignalingMessage(msg);
100267
101256
  } catch (err) {
100268
- log87.warn("Invalid signaling message", { error: String(err) });
101257
+ log89.warn("Invalid signaling message", { error: String(err) });
100269
101258
  }
100270
101259
  });
100271
101260
  ws.on("close", (code) => {
100272
- log87.warn("Signal server disconnected", { code });
101261
+ log89.warn("Signal server disconnected", { code });
100273
101262
  this.stopHeartbeat();
100274
101263
  this.ws = null;
100275
101264
  this.emitStatus();
@@ -100277,7 +101266,7 @@ var init_agent3 = __esm({
100277
101266
  this.scheduleReconnect();
100278
101267
  });
100279
101268
  ws.on("error", (err) => {
100280
- log87.error("Signal server error", { error: err.message });
101269
+ log89.error("Signal server error", { error: err.message });
100281
101270
  });
100282
101271
  }
100283
101272
  handleSignalingMessage(msg) {
@@ -100288,7 +101277,7 @@ var init_agent3 = __esm({
100288
101277
  this.send({ type: "pong" });
100289
101278
  break;
100290
101279
  case "registered":
100291
- log87.info("Registered with signal server", { instanceId: msg["instanceId"] });
101280
+ log89.info("Registered with signal server", { instanceId: msg["instanceId"] });
100292
101281
  break;
100293
101282
  case "peer_request":
100294
101283
  if (peerId)
@@ -100310,7 +101299,7 @@ var init_agent3 = __esm({
100310
101299
  break;
100311
101300
  case "relay_activated":
100312
101301
  if (peerId)
100313
- log87.info("Peer activated relay mode", { peerId });
101302
+ log89.info("Peer activated relay mode", { peerId });
100314
101303
  break;
100315
101304
  case "relay_frame":
100316
101305
  if (peerId && msg["data"]) {
@@ -100318,21 +101307,21 @@ var init_agent3 = __esm({
100318
101307
  }
100319
101308
  break;
100320
101309
  default:
100321
- log87.debug("Unknown signaling message type", { type });
101310
+ log89.debug("Unknown signaling message type", { type });
100322
101311
  }
100323
101312
  }
100324
101313
  // ── WebRTC Peer Connections ───────────────────────────────────────────────
100325
101314
  handlePeerRequest(peerId) {
100326
- log87.info("Peer connection requested", { peerId });
101315
+ log89.info("Peer connection requested", { peerId });
100327
101316
  this.createPeerConnection(peerId);
100328
101317
  }
100329
101318
  handleOffer(peerId, sdp) {
100330
101319
  let session = this.peers.get(peerId);
100331
101320
  if (!session) {
100332
- log87.info("Received offer, creating new peer connection", { peerId });
101321
+ log89.info("Received offer, creating new peer connection", { peerId });
100333
101322
  session = this.createPeerConnection(peerId);
100334
101323
  } else if (!session.pc) {
100335
- log87.info("Received offer for relay-only peer, upgrading to P2P", { peerId });
101324
+ log89.info("Received offer for relay-only peer, upgrading to P2P", { peerId });
100336
101325
  const newSession = this.createPeerConnection(peerId);
100337
101326
  newSession.markusToken = session.markusToken;
100338
101327
  newSession.connectedAt = session.connectedAt;
@@ -100341,14 +101330,14 @@ var init_agent3 = __esm({
100341
101330
  clearInterval(session.pingTimer);
100342
101331
  session = newSession;
100343
101332
  } else {
100344
- log87.info("Received offer for existing peer (ICE restart)", { peerId });
101333
+ log89.info("Received offer for existing peer (ICE restart)", { peerId });
100345
101334
  }
100346
101335
  session.pc.setRemoteDescription(sdp, getRtcModule().DescriptionType.Offer);
100347
101336
  }
100348
101337
  handleIce(peerId, candidate, mid) {
100349
101338
  const session = this.peers.get(peerId);
100350
101339
  if (!session?.pc) {
100351
- log87.warn("Received ICE candidate but no PC", { peerId, hasSession: !!session });
101340
+ log89.warn("Received ICE candidate but no PC", { peerId, hasSession: !!session });
100352
101341
  return;
100353
101342
  }
100354
101343
  session.pc.addRemoteCandidate(candidate, mid ?? "0");
@@ -100387,25 +101376,25 @@ var init_agent3 = __esm({
100387
101376
  const session = { pc, dc: null, pendingChunks: /* @__PURE__ */ new Map(), markusToken: null, connectedAt: now3, lastActiveAt: now3, pingTimer: null, lastPong: now3 };
100388
101377
  this.peers.set(peerId, session);
100389
101378
  pc.onStateChange((state) => {
100390
- log87.info("Peer RTC state", { peerId, state });
101379
+ log89.info("Peer RTC state", { peerId, state });
100391
101380
  if (state === "failed" || state === "closed") {
100392
101381
  this.handlePcFailed(peerId);
100393
101382
  }
100394
101383
  this.emitStatus();
100395
101384
  });
100396
101385
  pc.onGatheringStateChange((state) => {
100397
- log87.info("ICE gathering", { peerId, state });
101386
+ log89.info("ICE gathering", { peerId, state });
100398
101387
  });
100399
101388
  pc.onLocalDescription((sdp, type) => {
100400
- log87.info("Sending local description", { peerId, type });
101389
+ log89.info("Sending local description", { peerId, type });
100401
101390
  this.send({ type, peerId, sdp });
100402
101391
  });
100403
101392
  pc.onLocalCandidate((candidate, mid) => {
100404
- log87.info("Sending ICE candidate", { peerId, candidate: candidate.slice(0, 60) });
101393
+ log89.info("Sending ICE candidate", { peerId, candidate: candidate.slice(0, 60) });
100405
101394
  this.send({ type: "ice", peerId, candidate, mid });
100406
101395
  });
100407
101396
  pc.onDataChannel((dc) => {
100408
- log87.info("DataChannel opened", { peerId, label: dc.getLabel() });
101397
+ log89.info("DataChannel opened", { peerId, label: dc.getLabel() });
100409
101398
  session.dc = dc;
100410
101399
  session.lastPong = Date.now();
100411
101400
  this.emitStatus();
@@ -100415,7 +101404,7 @@ var init_agent3 = __esm({
100415
101404
  this.handleDataChannelMessage(peerId, data);
100416
101405
  });
100417
101406
  dc.onClosed(() => {
100418
- log87.info("DataChannel closed, keeping session for relay", { peerId });
101407
+ log89.info("DataChannel closed, keeping session for relay", { peerId });
100419
101408
  session.dc = null;
100420
101409
  this.emitStatus();
100421
101410
  });
@@ -100426,7 +101415,7 @@ var init_agent3 = __esm({
100426
101415
  const session = this.peers.get(peerId);
100427
101416
  if (!session)
100428
101417
  return;
100429
- log87.info("WebRTC failed, keeping session alive for relay", { peerId });
101418
+ log89.info("WebRTC failed, keeping session alive for relay", { peerId });
100430
101419
  try {
100431
101420
  session.dc?.close();
100432
101421
  } catch {
@@ -100460,7 +101449,7 @@ var init_agent3 = __esm({
100460
101449
  }
100461
101450
  this.peers.delete(peerId);
100462
101451
  this.emitStatus();
100463
- log87.info("Peer cleaned up", { peerId });
101452
+ log89.info("Peer cleaned up", { peerId });
100464
101453
  }
100465
101454
  startPeerPing(peerId, session) {
100466
101455
  if (session.pingTimer)
@@ -100470,13 +101459,13 @@ var init_agent3 = __esm({
100470
101459
  session.pingTimer = setInterval(() => {
100471
101460
  const now3 = Date.now();
100472
101461
  if (now3 - session.lastActiveAt > RELAY_INACTIVITY_TIMEOUT_MS) {
100473
- log87.info("Peer inactive for too long, cleaning up", { peerId });
101462
+ log89.info("Peer inactive for too long, cleaning up", { peerId });
100474
101463
  this.cleanupPeer(peerId);
100475
101464
  return;
100476
101465
  }
100477
101466
  const elapsed = now3 - session.lastPong;
100478
101467
  if (elapsed > PEER_PING_INTERVAL_MS + PEER_PING_TIMEOUT_MS) {
100479
- log87.warn("Peer ping timeout, unresponsive", { peerId, elapsed });
101468
+ log89.warn("Peer ping timeout, unresponsive", { peerId, elapsed });
100480
101469
  this.cleanupPeer(peerId);
100481
101470
  return;
100482
101471
  }
@@ -100517,12 +101506,12 @@ var init_agent3 = __esm({
100517
101506
  this.sendToPeer(peerId, { type: "error", error: `Unknown message type: ${type}` });
100518
101507
  }
100519
101508
  } catch (err) {
100520
- log87.warn("Invalid DataChannel message", { peerId, error: String(err) });
101509
+ log89.warn("Invalid DataChannel message", { peerId, error: String(err) });
100521
101510
  }
100522
101511
  }
100523
101512
  handleRelayFrame(peerId, data) {
100524
101513
  if (!this.peers.has(peerId)) {
100525
- log87.info("Relay frame from unknown peer, creating relay-only session", { peerId });
101514
+ log89.info("Relay frame from unknown peer, creating relay-only session", { peerId });
100526
101515
  const now3 = Date.now();
100527
101516
  this.peers.set(peerId, {
100528
101517
  pc: null,
@@ -100716,14 +101705,14 @@ var init_agent3 = __esm({
100716
101705
  session.dc.sendMessage(data);
100717
101706
  return;
100718
101707
  } catch (err) {
100719
- log87.warn("DataChannel send failed, falling back to relay", { peerId, error: String(err) });
101708
+ log89.warn("DataChannel send failed, falling back to relay", { peerId, error: String(err) });
100720
101709
  }
100721
101710
  }
100722
101711
  if (this.ws?.readyState === WebSocket2.OPEN) {
100723
101712
  this.send({ type: "relay_frame", peerId, data });
100724
101713
  return;
100725
101714
  }
100726
- log87.warn("No transport available for peer", { peerId });
101715
+ log89.warn("No transport available for peer", { peerId });
100727
101716
  }
100728
101717
  send(msg) {
100729
101718
  if (this.ws?.readyState === WebSocket2.OPEN) {
@@ -100734,7 +101723,7 @@ var init_agent3 = __esm({
100734
101723
  this.stopHeartbeat();
100735
101724
  this.heartbeatTimer = setInterval(() => {
100736
101725
  this.send({ type: "pong" });
100737
- }, HEARTBEAT_INTERVAL_MS);
101726
+ }, HEARTBEAT_INTERVAL_MS2);
100738
101727
  }
100739
101728
  stopHeartbeat() {
100740
101729
  if (this.heartbeatTimer) {
@@ -100747,7 +101736,7 @@ var init_agent3 = __esm({
100747
101736
  return;
100748
101737
  const delay = Math.min(RECONNECT_BASE_MS * Math.pow(2, this.reconnectAttempts), RECONNECT_MAX_MS);
100749
101738
  this.reconnectAttempts++;
100750
- log87.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})...`);
101739
+ log89.info(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})...`);
100751
101740
  this.reconnectTimer = setTimeout(() => this.start(), delay);
100752
101741
  }
100753
101742
  emitStatus() {
@@ -100780,15 +101769,15 @@ var start_exports = {};
100780
101769
  __export(start_exports, {
100781
101770
  registerStartCommand: () => registerStartCommand
100782
101771
  });
100783
- import { resolve as resolve18, join as join32, dirname as dirname11 } from "node:path";
100784
- import { existsSync as existsSync37, readFileSync as readFileSync26 } from "node:fs";
100785
- import { homedir as homedir22 } from "node:os";
101772
+ import { resolve as resolve18, join as join34, dirname as dirname13 } from "node:path";
101773
+ import { existsSync as existsSync39, readFileSync as readFileSync28 } from "node:fs";
101774
+ import { homedir as homedir24 } from "node:os";
100786
101775
  function registerStartCommand(program2) {
100787
101776
  program2.command("start").description("Start the Markus server (auto-initializes on first run)").option("--setup", "Force re-run the interactive setup wizard before starting").action(async (opts) => {
100788
101777
  const globalOpts = program2.optsWithGlobals();
100789
101778
  const configPath = globalOpts.config ?? getDefaultConfigPath();
100790
- if (opts.setup || !existsSync37(configPath)) {
100791
- if (!existsSync37(configPath)) {
101779
+ if (opts.setup || !existsSync39(configPath)) {
101780
+ if (!existsSync39(configPath)) {
100792
101781
  console.log(" No configuration found \u2014 auto-configuring from environment...\n");
100793
101782
  }
100794
101783
  const { quickInit: quickInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
@@ -100917,8 +101906,8 @@ async function createServices(config) {
100917
101906
  extraSkillDirs: skillDirs
100918
101907
  });
100919
101908
  const storage = await initStorage(config.database?.url);
100920
- const markusDataDir = join32(homedir22(), ".markus");
100921
- const sharedDataDir = join32(markusDataDir, "shared");
101909
+ const markusDataDir = join34(homedir24(), ".markus");
101910
+ const sharedDataDir = join34(markusDataDir, "shared");
100922
101911
  const taskService = new TaskService();
100923
101912
  taskService.setSharedDataDir(sharedDataDir);
100924
101913
  if (storage) {
@@ -100950,7 +101939,7 @@ async function createServices(config) {
100950
101939
  const agentManager = new AgentManager({
100951
101940
  llmRouter,
100952
101941
  roleLoader,
100953
- dataDir: join32(markusDataDir, "agents"),
101942
+ dataDir: join34(markusDataDir, "agents"),
100954
101943
  sharedDataDir,
100955
101944
  skillRegistry,
100956
101945
  taskService,
@@ -100990,8 +101979,10 @@ async function createServices(config) {
100990
101979
  const hitlService = new HITLService();
100991
101980
  hitlService.setOrgService(orgService);
100992
101981
  taskService.setHITLService(hitlService);
101982
+ const licenseService = new LicenseService(config.hub?.url);
101983
+ const telemetryService = new TelemetryService(config.hub?.url ?? "https://markus.global", licenseService.getInstanceId());
100993
101984
  const billingService = new BillingService();
100994
- billingService.setOrgPlan("default", "free");
101985
+ billingService.setOrgPlan("default", licenseService.getPlan());
100995
101986
  const auditService = new AuditService();
100996
101987
  taskService.setAuditService(auditService);
100997
101988
  if (storage?.auditRepo) {
@@ -101006,6 +101997,8 @@ async function createServices(config) {
101006
101997
  skillRegistry,
101007
101998
  hitlService,
101008
101999
  billingService,
102000
+ licenseService,
102001
+ telemetryService,
101009
102002
  auditService,
101010
102003
  bootstrapOwnerId
101011
102004
  };
@@ -101065,10 +102058,10 @@ async function startServer(config, values) {
101065
102058
  startupLog("INFO", "\u6B63\u5728\u542F\u52A8\u670D\u52A1...");
101066
102059
  const currentPath = process.env["PATH"] ?? "";
101067
102060
  const extraPaths = [];
101068
- const selfBinDir = dirname11(resolve18(process.argv[1] ?? ""));
102061
+ const selfBinDir = dirname13(resolve18(process.argv[1] ?? ""));
101069
102062
  if (selfBinDir && !currentPath.includes(selfBinDir)) extraPaths.push(selfBinDir);
101070
- const cwdBin = join32(process.cwd(), "node_modules", ".bin");
101071
- if (existsSync37(cwdBin) && !currentPath.includes(cwdBin)) extraPaths.push(cwdBin);
102063
+ const cwdBin = join34(process.cwd(), "node_modules", ".bin");
102064
+ if (existsSync39(cwdBin) && !currentPath.includes(cwdBin)) extraPaths.push(cwdBin);
101072
102065
  if (extraPaths.length > 0) {
101073
102066
  process.env["PATH"] = `${extraPaths.join(":")}:${currentPath}`;
101074
102067
  }
@@ -101111,6 +102104,8 @@ async function startServer(config, values) {
101111
102104
  skillRegistry,
101112
102105
  hitlService,
101113
102106
  billingService,
102107
+ licenseService,
102108
+ telemetryService,
101114
102109
  auditService,
101115
102110
  bootstrapOwnerId
101116
102111
  } = await createServices(config);
@@ -101120,6 +102115,8 @@ async function startServer(config, values) {
101120
102115
  apiServer.setSkillRegistry(skillRegistry);
101121
102116
  apiServer.setHITLService(hitlService);
101122
102117
  apiServer.setBillingService(billingService);
102118
+ apiServer.setLicenseService(licenseService);
102119
+ apiServer.setTelemetryService(telemetryService);
101123
102120
  apiServer.setAuditService(auditService);
101124
102121
  const projectService = new ProjectService();
101125
102122
  const storage = orgService.getStorage();
@@ -101133,7 +102130,7 @@ async function startServer(config, values) {
101133
102130
  projectService.setProjectRepo(storage.projectRepo);
101134
102131
  }
101135
102132
  await projectService.loadFromDB("default");
101136
- const knowledgeStore = new FileKnowledgeStore(join32(homedir22(), ".markus", "knowledge"));
102133
+ const knowledgeStore = new FileKnowledgeStore(join34(homedir24(), ".markus", "knowledge"));
101137
102134
  const knowledgeService = new KnowledgeService(knowledgeStore);
101138
102135
  const deliverableService = new DeliverableService(storage?.deliverableRepo);
101139
102136
  await deliverableService.load();
@@ -101200,17 +102197,28 @@ async function startServer(config, values) {
101200
102197
  await modelCatalog.initialize();
101201
102198
  apiServer.setModelCatalog(modelCatalog);
101202
102199
  if (config.hub?.url) apiServer.setHubUrl(config.hub.url);
102200
+ telemetryService.setStatsProvider(() => {
102201
+ const am = orgService.getAgentManager();
102202
+ return {
102203
+ agentCount: am ? am.listAgents().length : 0,
102204
+ taskCount: taskService.listTasks().length,
102205
+ toolCallCount: billingService.getUsageSummary("default").toolCalls,
102206
+ teamCount: orgService.listTeams("default").length,
102207
+ plan: licenseService.getPlan()
102208
+ };
102209
+ });
102210
+ telemetryService.start();
101203
102211
  const webUiDir = resolveWebUiDir();
101204
102212
  if (webUiDir) {
101205
102213
  apiServer.setWebUiDir(webUiDir);
101206
- log88.info("Web UI static files enabled", { dir: webUiDir });
102214
+ log90.info("Web UI static files enabled", { dir: webUiDir });
101207
102215
  }
101208
102216
  {
101209
102217
  const { LocalFileStorageProvider: LocalFileStorageProvider2 } = await Promise.resolve().then(() => (init_dist6(), dist_exports5));
101210
102218
  const localDir = config.fileStorage?.local?.dir;
101211
102219
  const fileStorage = new LocalFileStorageProvider2(localDir ?? void 0);
101212
102220
  apiServer.setFileStorage(fileStorage);
101213
- log88.info("File storage initialized", { provider: "local", dir: localDir ?? "~/.markus/uploads" });
102221
+ log90.info("File storage initialized", { provider: "local", dir: localDir ?? "~/.markus/uploads" });
101214
102222
  }
101215
102223
  const firstOrgId = "default";
101216
102224
  let ownerUserId = bootstrapOwnerId;
@@ -101231,7 +102239,7 @@ async function startServer(config, values) {
101231
102239
  const builderService = apiServer.getBuilderService();
101232
102240
  if (builderService) {
101233
102241
  const builtinTeamsDir = resolveTemplatesDir("teams");
101234
- if (builtinTeamsDir && existsSync37(builtinTeamsDir)) {
102242
+ if (builtinTeamsDir && existsSync39(builtinTeamsDir)) {
101235
102243
  builderService.setBuiltinTeamTemplatesDir(builtinTeamsDir);
101236
102244
  }
101237
102245
  agentManager.setBuilderService(builderService);
@@ -101294,29 +102302,29 @@ async function startServer(config, values) {
101294
102302
  try {
101295
102303
  storage.chatSessionRepo.migrateLegacyMessages();
101296
102304
  } catch (e) {
101297
- log88.warn("Legacy chat message migration failed", { error: String(e) });
102305
+ log90.warn("Legacy chat message migration failed", { error: String(e) });
101298
102306
  }
101299
102307
  const defaultSessionUserId = ownerUserId;
101300
102308
  try {
101301
102309
  storage.chatSessionRepo.migrateNullUserSessions(defaultSessionUserId);
101302
102310
  } catch (e) {
101303
- log88.warn("NULL user_id session migration failed", { error: String(e) });
102311
+ log90.warn("NULL user_id session migration failed", { error: String(e) });
101304
102312
  }
101305
102313
  try {
101306
102314
  storage.chatSessionRepo.migrateDefaultUserSessions(defaultSessionUserId);
101307
102315
  } catch (e) {
101308
- log88.warn("'default' user_id session migration failed", { error: String(e) });
102316
+ log90.warn("'default' user_id session migration failed", { error: String(e) });
101309
102317
  }
101310
102318
  try {
101311
102319
  storage.notificationRepo.migrateDefaultUserId(defaultSessionUserId);
101312
102320
  } catch (e) {
101313
- log88.warn("'default' user_id notification migration failed", { error: String(e) });
102321
+ log90.warn("'default' user_id notification migration failed", { error: String(e) });
101314
102322
  }
101315
102323
  if (storage.approvalRepo) {
101316
102324
  try {
101317
102325
  storage.approvalRepo.migrateDefaultTargetUserId(defaultSessionUserId);
101318
102326
  } catch (e) {
101319
- log88.warn("'default' target_user_id approval migration failed", { error: String(e) });
102327
+ log90.warn("'default' target_user_id approval migration failed", { error: String(e) });
101320
102328
  }
101321
102329
  }
101322
102330
  for (const info2 of agentManager.listAgents()) {
@@ -101345,7 +102353,7 @@ async function startServer(config, values) {
101345
102353
  isMainSession: true
101346
102354
  }, defaultSessionUserId);
101347
102355
  } catch (e) {
101348
- log88.warn("Failed to persist activity log", { agentId: agentId2, error: String(e) });
102356
+ log90.warn("Failed to persist activity log", { agentId: agentId2, error: String(e) });
101349
102357
  }
101350
102358
  });
101351
102359
  agentManager.getEventBus().on("agent:notify-user", async (evt) => {
@@ -101398,7 +102406,7 @@ ${body}${contextSuffix}`;
101398
102406
  metadata: { agentId: agentId2, agentName: agent.config.name, taskId: taskId2, requirementId: requirementId2, sessionId: mainSession.id }
101399
102407
  });
101400
102408
  } catch (e) {
101401
- log88.warn("Failed to handle notify-user event", { agentId: agentId2, error: String(e) });
102409
+ log90.warn("Failed to handle notify-user event", { agentId: agentId2, error: String(e) });
101402
102410
  }
101403
102411
  });
101404
102412
  agentManager.getEventBus().on("agent:escalation", async (evt) => {
@@ -101440,7 +102448,7 @@ ${reason}`;
101440
102448
  success: false
101441
102449
  });
101442
102450
  } catch (e) {
101443
- log88.warn("Failed to handle escalation event", { agentId: agentId2, error: String(e) });
102451
+ log90.warn("Failed to handle escalation event", { agentId: agentId2, error: String(e) });
101444
102452
  }
101445
102453
  });
101446
102454
  agentManager.getEventBus().on("agent:created", (evt) => {
@@ -101486,7 +102494,7 @@ ${reason}`;
101486
102494
  heartbeatIntervalMs: agent.config.heartbeatIntervalMs
101487
102495
  });
101488
102496
  } catch (err) {
101489
- log88.warn("Failed to persist gateway agent to DB (may already exist)", { error: String(err) });
102497
+ log90.warn("Failed to persist gateway agent to DB (may already exist)", { error: String(err) });
101490
102498
  }
101491
102499
  }
101492
102500
  return { id: agent.id };
@@ -101531,11 +102539,11 @@ ${reason}`;
101531
102539
  }));
101532
102540
  });
101533
102541
  apiServer.setGateway(gateway, gatewaySecret);
101534
- log88.info("External Agent Gateway enabled", { secret: gatewaySecret === "markus-gateway-default-secret-change-me" ? "(default)" : "(custom)" });
102542
+ log90.info("External Agent Gateway enabled", { secret: gatewaySecret === "markus-gateway-default-secret-change-me" ? "(default)" : "(custom)" });
101535
102543
  {
101536
- const hubTokenPath = join32(homedir22(), ".markus", "hub-token");
102544
+ const hubTokenPath = join34(homedir24(), ".markus", "hub-token");
101537
102545
  const createRemoteAgent = async () => {
101538
- const token = existsSync37(hubTokenPath) ? readFileSync26(hubTokenPath, "utf-8").trim() : void 0;
102546
+ const token = existsSync39(hubTokenPath) ? readFileSync28(hubTokenPath, "utf-8").trim() : void 0;
101539
102547
  if (!token) return null;
101540
102548
  const { RemoteAccessAgent: RemoteAccessAgent2 } = await Promise.resolve().then(() => (init_dist8(), dist_exports6));
101541
102549
  return new RemoteAccessAgent2({
@@ -101547,7 +102555,7 @@ ${reason}`;
101547
102555
  });
101548
102556
  };
101549
102557
  apiServer.setRemoteAgentFactory(createRemoteAgent);
101550
- if (config.remote?.enabled !== false) {
102558
+ if (config.remote?.enabled === true) {
101551
102559
  const remoteAgent = await createRemoteAgent();
101552
102560
  if (remoteAgent) {
101553
102561
  apiServer.setRemoteAgent(remoteAgent);
@@ -101555,14 +102563,14 @@ ${reason}`;
101555
102563
  remoteAgent.start().then(() => {
101556
102564
  const status = remoteAgent.getStatus();
101557
102565
  if (status.remoteUrl) {
101558
- log88.info(`Remote access available at ${status.remoteUrl}`);
102566
+ log90.info(`Remote access available at ${status.remoteUrl}`);
101559
102567
  }
101560
102568
  }).catch((err) => {
101561
- log88.warn("Remote access failed to start", { error: String(err) });
102569
+ log90.warn("Remote access failed to start", { error: String(err) });
101562
102570
  });
101563
102571
  }
101564
102572
  } else {
101565
- log88.debug("Remote access: no Hub token yet (can enable later via Settings)");
102573
+ log90.debug("Remote access: no Hub token yet (can enable later via Settings)");
101566
102574
  }
101567
102575
  }
101568
102576
  }
@@ -101573,7 +102581,7 @@ ${reason}`;
101573
102581
  const scheduledTaskRunner = new ScheduledTaskRunner(taskService);
101574
102582
  scheduledTaskRunner.start();
101575
102583
  agentManager.setEscalationHandler((agentId2, reason) => {
101576
- log88.warn("Agent escalation", { agentId: agentId2, reason });
102584
+ log90.warn("Agent escalation", { agentId: agentId2, reason });
101577
102585
  });
101578
102586
  agentManager.setApprovalHandler(async (agentId2, request) => {
101579
102587
  const agents = agentManager.listAgents();
@@ -101640,6 +102648,17 @@ ${reason}`;
101640
102648
  });
101641
102649
  }
101642
102650
  });
102651
+ billingService.setToolCallsTodayProvider(() => {
102652
+ let total = 0;
102653
+ for (const a of agentManager.listAgents()) {
102654
+ try {
102655
+ total += agentManager.getAgent(a.id).getUsageStats().toolCallsToday;
102656
+ } catch {
102657
+ }
102658
+ }
102659
+ return total;
102660
+ });
102661
+ agentManager.setToolCallLimitChecker(() => billingService.checkLimit("default", "tool_call"));
101643
102662
  if (storage) {
101644
102663
  agentManager.setStateChangeHandler(async (agentId2, state) => {
101645
102664
  try {
@@ -101648,7 +102667,7 @@ ${reason}`;
101648
102667
  state.status
101649
102668
  );
101650
102669
  } catch (err) {
101651
- log88.warn("Failed to persist agent state", { agentId: agentId2, error: String(err) });
102670
+ log90.warn("Failed to persist agent state", { agentId: agentId2, error: String(err) });
101652
102671
  }
101653
102672
  apiServer.getWSBroadcaster().broadcastAgentUpdate(agentId2, state.status, {
101654
102673
  lastError: state.lastError,
@@ -101673,7 +102692,7 @@ ${reason}`;
101673
102692
  startedAt: activity.startedAt
101674
102693
  });
101675
102694
  } catch (err) {
101676
- log88.warn("Failed to persist activity start", { activityId: activity.id, error: String(err) });
102695
+ log90.warn("Failed to persist activity start", { activityId: activity.id, error: String(err) });
101677
102696
  }
101678
102697
  },
101679
102698
  onLog: (data) => {
@@ -101689,20 +102708,20 @@ ${reason}`;
101689
102708
  metadata: data.metadata
101690
102709
  });
101691
102710
  } catch (err) {
101692
- log88.warn("Failed to persist execution stream activity log", { activityId: data.activityId, error: String(err) });
102711
+ log90.warn("Failed to persist execution stream activity log", { activityId: data.activityId, error: String(err) });
101693
102712
  }
101694
102713
  }
101695
102714
  try {
101696
102715
  actRepo.insertActivityLog(data);
101697
102716
  } catch (err) {
101698
- log88.warn("Failed to persist activity log", { activityId: data.activityId, error: String(err) });
102717
+ log90.warn("Failed to persist activity log", { activityId: data.activityId, error: String(err) });
101699
102718
  }
101700
102719
  },
101701
102720
  onEnd: (activityId, summary) => {
101702
102721
  try {
101703
102722
  actRepo.updateActivity(activityId, summary);
101704
102723
  } catch (err) {
101705
- log88.warn("Failed to persist activity end", { activityId, error: String(err) });
102724
+ log90.warn("Failed to persist activity end", { activityId, error: String(err) });
101706
102725
  }
101707
102726
  }
101708
102727
  });
@@ -101745,14 +102764,14 @@ ${reason}`;
101745
102764
  queuedAt: item.queuedAt
101746
102765
  });
101747
102766
  } catch (e) {
101748
- log88.warn("Failed to persist mailbox item", { id: item.id, error: String(e) });
102767
+ log90.warn("Failed to persist mailbox item", { id: item.id, error: String(e) });
101749
102768
  }
101750
102769
  },
101751
102770
  updateStatus: (itemId, status, extra) => {
101752
102771
  try {
101753
102772
  mbRepo.updateStatus(itemId, status, extra);
101754
102773
  } catch (e) {
101755
- log88.warn("Failed to update mailbox status", { itemId, error: String(e) });
102774
+ log90.warn("Failed to update mailbox status", { itemId, error: String(e) });
101756
102775
  }
101757
102776
  },
101758
102777
  markStaleProcessingAsDropped: (aid) => mbRepo.markStaleProcessingAsDropped(aid),
@@ -101795,7 +102814,7 @@ ${reason}`;
101795
102814
  }
101796
102815
  });
101797
102816
  const { dropped, restored, expired, merged } = mailbox.recoverStaleItems();
101798
- if (dropped > 0 || restored > 0 || expired > 0 || merged > 0) log88.info("Mailbox recovery on startup", { agentId: agentId2, dropped, restored, expired, merged });
102817
+ if (dropped > 0 || restored > 0 || expired > 0 || merged > 0) log90.info("Mailbox recovery on startup", { agentId: agentId2, dropped, restored, expired, merged });
101799
102818
  agent.getAttentionController().setDecisionPersistence({
101800
102819
  save: (decision) => {
101801
102820
  try {
@@ -101810,7 +102829,7 @@ ${reason}`;
101810
102829
  createdAt: decision.createdAt
101811
102830
  });
101812
102831
  } catch (e) {
101813
- log88.warn("Failed to persist decision", { id: decision.id, error: String(e) });
102832
+ log90.warn("Failed to persist decision", { id: decision.id, error: String(e) });
101814
102833
  }
101815
102834
  }
101816
102835
  });
@@ -101905,7 +102924,7 @@ ${reason}`;
101905
102924
  llmConfig: agent.config.llmConfig,
101906
102925
  heartbeatIntervalMs: agent.config.heartbeatIntervalMs
101907
102926
  }).catch((err) => {
101908
- log88.warn("Failed to persist runtime-created agent to DB", { agentId: agentId2, error: String(err) });
102927
+ log90.warn("Failed to persist runtime-created agent to DB", { agentId: agentId2, error: String(err) });
101909
102928
  });
101910
102929
  } catch {
101911
102930
  }
@@ -101988,7 +103007,7 @@ ${reason}`;
101988
103007
  const nextMidnight = new Date(now3.getFullYear(), now3.getMonth(), now3.getDate() + 1, 0, 0, 0, 0);
101989
103008
  const msUntilMidnight = nextMidnight.getTime() - now3.getTime();
101990
103009
  setTimeout(() => {
101991
- log88.info("Daily token reset triggered");
103010
+ log90.info("Daily token reset triggered");
101992
103011
  for (const agentInfo of agentManager.listAgents()) {
101993
103012
  try {
101994
103013
  const agent = agentManager.getAgent(agentInfo.id);
@@ -101998,7 +103017,7 @@ ${reason}`;
101998
103017
  }
101999
103018
  scheduleDailyReset();
102000
103019
  }, msUntilMidnight);
102001
- log88.info(`Daily token reset scheduled in ${Math.round(msUntilMidnight / 6e4)} minutes`);
103020
+ log90.info(`Daily token reset scheduled in ${Math.round(msUntilMidnight / 6e4)} minutes`);
102002
103021
  };
102003
103022
  scheduleDailyReset();
102004
103023
  const messageRouter = new MessageRouter();
@@ -102029,7 +103048,7 @@ ${reason}`;
102029
103048
  durationMs: Date.now() - startTs,
102030
103049
  success: false
102031
103050
  });
102032
- log88.error("Agent message handler error", { error: String(error) });
103051
+ log90.error("Agent message handler error", { error: String(error) });
102033
103052
  return void 0;
102034
103053
  }
102035
103054
  });
@@ -102065,7 +103084,7 @@ ${reason}`;
102065
103084
  if (info2.updateAvailable) {
102066
103085
  console.log(`
102067
103086
  \x1B[33m\u2B06 New version available: v${info2.latestVersion} (current: v${info2.currentVersion})\x1B[0m`);
102068
- console.log(` Run \x1B[1mnpm i -g @markus-global/cli\x1B[0m to upgrade
103087
+ console.log(` Visit \x1B[1mhttps://markus.global/download\x1B[0m to download the latest version
102069
103088
  `);
102070
103089
  }
102071
103090
  }).catch(() => {
@@ -102074,7 +103093,7 @@ ${reason}`;
102074
103093
  try {
102075
103094
  await taskService.resumeInProgressTasks();
102076
103095
  } catch (err) {
102077
- log88.warn("Failed to auto-resume in_progress tasks", { error: String(err) });
103096
+ log90.warn("Failed to auto-resume in_progress tasks", { error: String(err) });
102078
103097
  }
102079
103098
  });
102080
103099
  process.on("SIGINT", () => {
@@ -102090,7 +103109,7 @@ ${reason}`;
102090
103109
  await new Promise(() => {
102091
103110
  });
102092
103111
  }
102093
- var log88;
103112
+ var log90;
102094
103113
  var init_start = __esm({
102095
103114
  "src/commands/start.ts"() {
102096
103115
  "use strict";
@@ -102102,7 +103121,7 @@ var init_start = __esm({
102102
103121
  init_logger2();
102103
103122
  init_browser();
102104
103123
  init_startupProgress();
102105
- log88 = createLogger("cli");
103124
+ log90 = createLogger("cli");
102106
103125
  }
102107
103126
  });
102108
103127
 
@@ -102731,8 +103750,8 @@ ${C3.BOLD}${C3.CYAN}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
102731
103750
  }
102732
103751
  }
102733
103752
  section("Storage");
102734
- const { homedir: homedir23 } = await import("node:os");
102735
- const storageDir = `${homedir23()}/.markus`;
103753
+ const { homedir: homedir25 } = await import("node:os");
103754
+ const storageDir = `${homedir25()}/.markus`;
102736
103755
  const dataFile = `${storageDir}/data.db`;
102737
103756
  try {
102738
103757
  if (!fs.existsSync(storageDir)) {
@@ -102756,7 +103775,7 @@ ${C3.BOLD}${C3.CYAN}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
102756
103775
  checkFail(`Storage check failed: ${e}`);
102757
103776
  }
102758
103777
  section("Skills");
102759
- const skillsDir = `${homedir23()}/.markus/skills`;
103778
+ const skillsDir = `${homedir25()}/.markus/skills`;
102760
103779
  if (fs.existsSync(skillsDir)) {
102761
103780
  try {
102762
103781
  const entries2 = fs.readdirSync(skillsDir);
@@ -102835,9 +103854,9 @@ ${C3.BOLD}\u25C6 Summary${C3.RESET}
102835
103854
  }
102836
103855
  }
102837
103856
  async function getDefaultConfigPath2() {
102838
- const { homedir: homedir23 } = await import("node:os");
102839
- const { join: join33 } = await import("node:path");
102840
- return join33(homedir23(), ".markus", "markus.json");
103857
+ const { homedir: homedir25 } = await import("node:os");
103858
+ const { join: join35 } = await import("node:path");
103859
+ return join35(homedir25(), ".markus", "markus.json");
102841
103860
  }
102842
103861
  function registerDoctorCommand(program2) {
102843
103862
  program2.command("doctor").description("Diagnose Markus configuration issues and environment health").option("--fix", "Attempt to automatically fix issues").option("--verbose", "Show detailed output").action(async (opts) => {
@@ -103339,19 +104358,19 @@ __export(install_agent_exports, {
103339
104358
  import { execSync as execSync5 } from "node:child_process";
103340
104359
  import { randomBytes as randomBytes6 } from "node:crypto";
103341
104360
  function registerInstallAgentCommands(program2) {
103342
- program2.command("install <platform>").description("Install an external agent platform and connect it to Markus").option("--org-id <id>", "Organization ID", "default").option("--agent-name <name>", "Agent display name").option("--skip-install", "Skip npm install (platform already installed)").option("--skip-init", "Skip platform initialization").option("--skip-connect", "Only install, do not register with Markus").action(async (platform4, opts, cmd) => {
104361
+ program2.command("install <platform>").description("Install an external agent platform and connect it to Markus").option("--org-id <id>", "Organization ID", "default").option("--agent-name <name>", "Agent display name").option("--skip-install", "Skip npm install (platform already installed)").option("--skip-init", "Skip platform initialization").option("--skip-connect", "Only install, do not register with Markus").action(async (platform5, opts, cmd) => {
103343
104362
  const g = cmd.optsWithGlobals();
103344
- const connector = findConnector(platform4);
104363
+ const connector = findConnector(platform5);
103345
104364
  if (!connector) {
103346
104365
  const available = loadConnectors().map((c) => c.platform).join(", ");
103347
- fail(`Unknown platform "${platform4}". Available: ${available || "none"}`);
104366
+ fail(`Unknown platform "${platform5}". Available: ${available || "none"}`);
103348
104367
  return;
103349
104368
  }
103350
104369
  console.log(`
103351
104370
  Installing ${connector.displayName}...
103352
104371
  `);
103353
104372
  const scan = scanInstalledPlatforms();
103354
- const existing = scan.find((s2) => s2.platform === platform4);
104373
+ const existing = scan.find((s2) => s2.platform === platform5);
103355
104374
  const alreadyInstalled = existing?.installed;
103356
104375
  if (alreadyInstalled && !opts.skipInstall) {
103357
104376
  console.log(` [1/5] ${connector.displayName} is already installed.`);
@@ -103387,13 +104406,13 @@ function registerInstallAgentCommands(program2) {
103387
104406
  console.log(` [4/5] Token generation skipped.`);
103388
104407
  console.log(` [5/5] Config write skipped.`);
103389
104408
  console.log(`
103390
- ${connector.displayName} installed. Run \`markus install ${platform4}\` again without --skip-connect to connect later.
104409
+ ${connector.displayName} installed. Run \`markus install ${platform5}\` again without --skip-connect to connect later.
103391
104410
  `);
103392
104411
  return;
103393
104412
  }
103394
104413
  const client = createClient(g);
103395
104414
  const serverUrl = g.server || process.env["MARKUS_API_URL"] || "http://localhost:8056";
103396
- const agentId2 = `${platform4}-${randomBytes6(4).toString("hex")}`;
104415
+ const agentId2 = `${platform5}-${randomBytes6(4).toString("hex")}`;
103397
104416
  const agentName = opts.agentName || connector.defaultAgentName || `${connector.displayName} Agent`;
103398
104417
  const capabilities = connector.defaultCapabilities ?? [];
103399
104418
  try {
@@ -103456,7 +104475,7 @@ function registerInstallAgentCommands(program2) {
103456
104475
  Connection failed: ${e.message}`);
103457
104476
  console.log(` ${connector.displayName} was installed but could not connect to Markus.`);
103458
104477
  console.log(` Make sure the Markus server is running (\`markus start\`), then run:`);
103459
- console.log(` markus install ${platform4}
104478
+ console.log(` markus install ${platform5}
103460
104479
  `);
103461
104480
  return;
103462
104481
  }
@@ -103552,14 +104571,14 @@ __export(system_exports, {
103552
104571
  registerSystemCommands: () => registerSystemCommands
103553
104572
  });
103554
104573
  import { execSync as execSync6 } from "node:child_process";
103555
- import { existsSync as existsSync38, readFileSync as readFileSync27 } from "node:fs";
103556
- import { resolve as resolve19, dirname as dirname12 } from "node:path";
104574
+ import { existsSync as existsSync40, readFileSync as readFileSync29 } from "node:fs";
104575
+ import { resolve as resolve19, dirname as dirname14 } from "node:path";
103557
104576
  import { fileURLToPath as fileURLToPath7 } from "node:url";
103558
104577
  function findMarkusRoot() {
103559
- let dir = dirname12(fileURLToPath7(import.meta.url));
104578
+ let dir = dirname14(fileURLToPath7(import.meta.url));
103560
104579
  for (let i = 0; i < 10; i++) {
103561
- if (existsSync38(resolve19(dir, "package.json")) && existsSync38(resolve19(dir, "packages"))) return dir;
103562
- dir = dirname12(dir);
104580
+ if (existsSync40(resolve19(dir, "package.json")) && existsSync40(resolve19(dir, "packages"))) return dir;
104581
+ dir = dirname14(dir);
103563
104582
  }
103564
104583
  return null;
103565
104584
  }
@@ -103609,13 +104628,13 @@ function registerSystemCommands(program2) {
103609
104628
  if (markusRoot) {
103610
104629
  if (!info2.currentVersion) {
103611
104630
  try {
103612
- const pkg = JSON.parse(readFileSync27(resolve19(markusRoot, "package.json"), "utf-8"));
104631
+ const pkg = JSON.parse(readFileSync29(resolve19(markusRoot, "package.json"), "utf-8"));
103613
104632
  info2.currentVersion = pkg.version;
103614
104633
  } catch {
103615
104634
  }
103616
104635
  }
103617
104636
  try {
103618
- const isGit = existsSync38(resolve19(markusRoot, ".git"));
104637
+ const isGit = existsSync40(resolve19(markusRoot, ".git"));
103619
104638
  if (isGit) {
103620
104639
  info2.gitBranch = execSync6("git rev-parse --abbrev-ref HEAD", { cwd: markusRoot, encoding: "utf-8" }).trim();
103621
104640
  info2.gitCommit = execSync6("git rev-parse --short HEAD", { cwd: markusRoot, encoding: "utf-8" }).trim();
@@ -103654,7 +104673,7 @@ function registerSystemCommands(program2) {
103654
104673
  fail("Cannot locate Markus installation directory");
103655
104674
  return;
103656
104675
  }
103657
- if (!existsSync38(resolve19(markusRoot, ".git"))) {
104676
+ if (!existsSync40(resolve19(markusRoot, ".git"))) {
103658
104677
  fail("Markus installation is not a git repository. Update manually.");
103659
104678
  return;
103660
104679
  }
@@ -103689,7 +104708,7 @@ var init_system = __esm({
103689
104708
 
103690
104709
  // src/index.ts
103691
104710
  import { resolve as resolve20 } from "node:path";
103692
- import { readFileSync as readFileSync28, existsSync as existsSync39 } from "node:fs";
104711
+ import { readFileSync as readFileSync30, existsSync as existsSync41 } from "node:fs";
103693
104712
  import process2 from "node:process";
103694
104713
 
103695
104714
  // ../../node_modules/.pnpm/commander@14.0.3/node_modules/commander/esm.mjs
@@ -103713,8 +104732,8 @@ var {
103713
104732
  init_dist();
103714
104733
  init_output();
103715
104734
  var envPath = resolve20(process2.cwd(), ".env");
103716
- if (existsSync39(envPath)) {
103717
- for (const line of readFileSync28(envPath, "utf-8").split("\n")) {
104735
+ if (existsSync41(envPath)) {
104736
+ for (const line of readFileSync30(envPath, "utf-8").split("\n")) {
103718
104737
  const trimmed = line.trim();
103719
104738
  if (!trimmed || trimmed.startsWith("#")) continue;
103720
104739
  const eqIdx = trimmed.indexOf("=");