@askexenow/exe-os 0.9.86 → 0.9.88
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/deploy/compose/docker-compose.yml +3 -3
- package/dist/bin/age-ontology-load.js +8 -2
- package/dist/bin/agentic-ontology-backfill.js +29 -0
- package/dist/bin/agentic-reflection-backfill.js +29 -0
- package/dist/bin/agentic-semantic-label.js +29 -0
- package/dist/bin/backfill-conversations.js +30 -0
- package/dist/bin/backfill-responses.js +30 -0
- package/dist/bin/backfill-vectors.js +30 -0
- package/dist/bin/bulk-sync-postgres.js +47 -1
- package/dist/bin/cc-doctor.js +3 -2
- package/dist/bin/cleanup-stale-review-tasks.js +30 -0
- package/dist/bin/cli.js +357 -19
- package/dist/bin/exe-agent.js +19 -0
- package/dist/bin/exe-assign.js +30 -0
- package/dist/bin/exe-boot.js +157 -4
- package/dist/bin/exe-call.js +20 -0
- package/dist/bin/exe-cloud.js +156 -3
- package/dist/bin/exe-dispatch.js +30 -1
- package/dist/bin/exe-doctor.js +30 -0
- package/dist/bin/exe-export-behaviors.js +29 -0
- package/dist/bin/exe-forget.js +30 -0
- package/dist/bin/exe-gateway.js +150 -35
- package/dist/bin/exe-healthcheck.js +2 -1
- package/dist/bin/exe-heartbeat.js +30 -0
- package/dist/bin/exe-kill.js +29 -0
- package/dist/bin/exe-launch-agent.js +29 -0
- package/dist/bin/exe-new-employee.js +37 -4
- package/dist/bin/exe-pending-messages.js +29 -0
- package/dist/bin/exe-pending-notifications.js +30 -0
- package/dist/bin/exe-pending-reviews.js +30 -0
- package/dist/bin/exe-rename.js +30 -0
- package/dist/bin/exe-review.js +30 -0
- package/dist/bin/exe-search.js +30 -0
- package/dist/bin/exe-session-cleanup.js +30 -1
- package/dist/bin/exe-settings.js +3 -0
- package/dist/bin/exe-start-codex.js +31 -2
- package/dist/bin/exe-start-opencode.js +31 -2
- package/dist/bin/exe-status.js +30 -0
- package/dist/bin/exe-team.js +30 -0
- package/dist/bin/git-sweep.js +30 -1
- package/dist/bin/graph-backfill.js +29 -0
- package/dist/bin/graph-export.js +29 -0
- package/dist/bin/graph-layer-benchmark.js +9 -1
- package/dist/bin/install.js +9 -0
- package/dist/bin/intercom-check.js +31 -1
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/postgres-agentic-reflection-backfill.js +7 -1
- package/dist/bin/postgres-agentic-semantic-backfill.js +7 -1
- package/dist/bin/registry-proxy.js +1 -0
- package/dist/bin/scan-tasks.js +31 -1
- package/dist/bin/setup.js +165 -9
- package/dist/bin/shard-migrate.js +29 -0
- package/dist/bin/stack-update.js +24 -7
- package/dist/bin/update.js +5 -0
- package/dist/gateway/index.js +30 -1
- package/dist/hooks/bug-report-worker.js +30 -1
- package/dist/hooks/codex-stop-task-finalizer.js +30 -1
- package/dist/hooks/commit-complete.js +30 -1
- package/dist/hooks/error-recall.js +29 -0
- package/dist/hooks/ingest.js +29 -0
- package/dist/hooks/instructions-loaded.js +29 -0
- package/dist/hooks/notification.js +29 -0
- package/dist/hooks/post-compact.js +29 -0
- package/dist/hooks/post-tool-combined.js +29 -0
- package/dist/hooks/pre-compact.js +30 -1
- package/dist/hooks/pre-tool-use.js +29 -0
- package/dist/hooks/prompt-submit.js +30 -1
- package/dist/hooks/session-end.js +30 -1
- package/dist/hooks/session-start.js +29 -0
- package/dist/hooks/stop.js +29 -0
- package/dist/hooks/subagent-stop.js +29 -0
- package/dist/hooks/summary-worker.js +155 -3
- package/dist/index.js +30 -1
- package/dist/lib/cloud-sync.js +136 -2
- package/dist/lib/consolidation.js +1 -0
- package/dist/lib/database.js +11 -0
- package/dist/lib/db.js +11 -0
- package/dist/lib/device-registry.js +11 -0
- package/dist/lib/employee-templates.js +19 -0
- package/dist/lib/exe-daemon.js +1455 -208
- package/dist/lib/hybrid-search.js +29 -0
- package/dist/lib/identity-templates.js +6 -2
- package/dist/lib/identity.js +1 -0
- package/dist/lib/messaging.js +2 -1
- package/dist/lib/reminders.js +1 -0
- package/dist/lib/schedules.js +29 -0
- package/dist/lib/skill-learning.js +1 -0
- package/dist/lib/store.js +29 -0
- package/dist/lib/tasks.js +2 -1
- package/dist/lib/tmux-routing.js +2 -1
- package/dist/lib/token-spend.js +1 -0
- package/dist/mcp/server.js +1278 -165
- package/dist/mcp/tools/complete-reminder.js +1 -0
- package/dist/mcp/tools/create-reminder.js +1 -0
- package/dist/mcp/tools/create-task.js +8 -3
- package/dist/mcp/tools/deactivate-behavior.js +1 -0
- package/dist/mcp/tools/list-reminders.js +1 -0
- package/dist/mcp/tools/list-tasks.js +1 -0
- package/dist/mcp/tools/send-message.js +2 -1
- package/dist/mcp/tools/update-task.js +2 -1
- package/dist/runtime/index.js +30 -1
- package/dist/tui/App.js +30 -1
- package/package.json +2 -2
package/dist/mcp/server.js
CHANGED
|
@@ -889,8 +889,8 @@ async function embedDirect(text3) {
|
|
|
889
889
|
const llamaCpp = await import("node-llama-cpp");
|
|
890
890
|
const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
891
891
|
const { existsSync: existsSync42 } = await import("fs");
|
|
892
|
-
const
|
|
893
|
-
const modelPath =
|
|
892
|
+
const path56 = await import("path");
|
|
893
|
+
const modelPath = path56.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
|
|
894
894
|
if (!existsSync42(modelPath)) {
|
|
895
895
|
throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
|
|
896
896
|
}
|
|
@@ -2180,6 +2180,7 @@ __export(database_exports, {
|
|
|
2180
2180
|
isInitialized: () => isInitialized,
|
|
2181
2181
|
setExternalClient: () => setExternalClient
|
|
2182
2182
|
});
|
|
2183
|
+
import { chmodSync as chmodSync2 } from "fs";
|
|
2183
2184
|
import { createClient } from "@libsql/client";
|
|
2184
2185
|
async function initDatabase(config2) {
|
|
2185
2186
|
if (_walCheckpointTimer) {
|
|
@@ -2221,6 +2222,16 @@ async function initDatabase(config2) {
|
|
|
2221
2222
|
if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
|
|
2222
2223
|
_adapterClient = await createPrismaDbAdapter(_resilientClient);
|
|
2223
2224
|
}
|
|
2225
|
+
try {
|
|
2226
|
+
chmodSync2(config2.dbPath, 384);
|
|
2227
|
+
for (const suffix of ["-wal", "-shm"]) {
|
|
2228
|
+
try {
|
|
2229
|
+
chmodSync2(config2.dbPath + suffix, 384);
|
|
2230
|
+
} catch {
|
|
2231
|
+
}
|
|
2232
|
+
}
|
|
2233
|
+
} catch {
|
|
2234
|
+
}
|
|
2224
2235
|
}
|
|
2225
2236
|
function isInitialized() {
|
|
2226
2237
|
return _adapterClient !== null || _client !== null;
|
|
@@ -3723,7 +3734,7 @@ async function tryKeytar() {
|
|
|
3723
3734
|
}
|
|
3724
3735
|
function deriveMachineKey() {
|
|
3725
3736
|
try {
|
|
3726
|
-
const
|
|
3737
|
+
const crypto21 = __require("crypto");
|
|
3727
3738
|
const material = [
|
|
3728
3739
|
os5.hostname(),
|
|
3729
3740
|
os5.userInfo().username,
|
|
@@ -3732,7 +3743,7 @@ function deriveMachineKey() {
|
|
|
3732
3743
|
// Machine ID on Linux (stable across reboots)
|
|
3733
3744
|
process.platform === "linux" ? readMachineId() : ""
|
|
3734
3745
|
].join("|");
|
|
3735
|
-
return
|
|
3746
|
+
return crypto21.createHash("sha256").update(material).digest();
|
|
3736
3747
|
} catch {
|
|
3737
3748
|
return null;
|
|
3738
3749
|
}
|
|
@@ -3746,9 +3757,9 @@ function readMachineId() {
|
|
|
3746
3757
|
}
|
|
3747
3758
|
}
|
|
3748
3759
|
function encryptWithMachineKey(plaintext, machineKey) {
|
|
3749
|
-
const
|
|
3750
|
-
const iv =
|
|
3751
|
-
const cipher =
|
|
3760
|
+
const crypto21 = __require("crypto");
|
|
3761
|
+
const iv = crypto21.randomBytes(12);
|
|
3762
|
+
const cipher = crypto21.createCipheriv("aes-256-gcm", machineKey, iv);
|
|
3752
3763
|
let encrypted = cipher.update(plaintext, "utf-8", "base64");
|
|
3753
3764
|
encrypted += cipher.final("base64");
|
|
3754
3765
|
const authTag = cipher.getAuthTag().toString("base64");
|
|
@@ -3757,13 +3768,13 @@ function encryptWithMachineKey(plaintext, machineKey) {
|
|
|
3757
3768
|
function decryptWithMachineKey(encrypted, machineKey) {
|
|
3758
3769
|
if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
|
|
3759
3770
|
try {
|
|
3760
|
-
const
|
|
3771
|
+
const crypto21 = __require("crypto");
|
|
3761
3772
|
const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
|
|
3762
3773
|
if (parts.length !== 3) return null;
|
|
3763
3774
|
const [ivB64, tagB64, cipherB64] = parts;
|
|
3764
3775
|
const iv = Buffer.from(ivB64, "base64");
|
|
3765
3776
|
const authTag = Buffer.from(tagB64, "base64");
|
|
3766
|
-
const decipher =
|
|
3777
|
+
const decipher = crypto21.createDecipheriv("aes-256-gcm", machineKey, iv);
|
|
3767
3778
|
decipher.setAuthTag(authTag);
|
|
3768
3779
|
let decrypted = decipher.update(cipherB64, "base64", "utf-8");
|
|
3769
3780
|
decrypted += decipher.final("utf-8");
|
|
@@ -4801,6 +4812,24 @@ var init_platform_procedures = __esm({
|
|
|
4801
4812
|
priority: "p0",
|
|
4802
4813
|
content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
|
|
4803
4814
|
},
|
|
4815
|
+
{
|
|
4816
|
+
title: "Bug report status check \u2014 surface available fixes on boot",
|
|
4817
|
+
domain: "support",
|
|
4818
|
+
priority: "p1",
|
|
4819
|
+
content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
4820
|
+
},
|
|
4821
|
+
{
|
|
4822
|
+
title: "Feature request triage \u2014 upstream feature vs local customization",
|
|
4823
|
+
domain: "support",
|
|
4824
|
+
priority: "p0",
|
|
4825
|
+
content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
|
|
4826
|
+
},
|
|
4827
|
+
{
|
|
4828
|
+
title: "Feature request status check \u2014 surface shipped features on boot",
|
|
4829
|
+
domain: "support",
|
|
4830
|
+
priority: "p1",
|
|
4831
|
+
content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
|
|
4832
|
+
},
|
|
4804
4833
|
// --- Operations ---
|
|
4805
4834
|
{
|
|
4806
4835
|
title: "Managers must supervise deployed workers",
|
|
@@ -7321,10 +7350,10 @@ async function hybridSearch(queryText, agentId, options) {
|
|
|
7321
7350
|
};
|
|
7322
7351
|
try {
|
|
7323
7352
|
const fs = await import("fs");
|
|
7324
|
-
const
|
|
7353
|
+
const path56 = await import("path");
|
|
7325
7354
|
const os21 = await import("os");
|
|
7326
|
-
const logPath =
|
|
7327
|
-
fs.mkdirSync(
|
|
7355
|
+
const logPath = path56.join(os21.homedir(), ".exe-os", "search-quality.jsonl");
|
|
7356
|
+
fs.mkdirSync(path56.dirname(logPath), { recursive: true });
|
|
7328
7357
|
fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
|
|
7329
7358
|
} catch {
|
|
7330
7359
|
}
|
|
@@ -8617,8 +8646,8 @@ __export(wiki_client_exports, {
|
|
|
8617
8646
|
listDocuments: () => listDocuments,
|
|
8618
8647
|
listWorkspaces: () => listWorkspaces
|
|
8619
8648
|
});
|
|
8620
|
-
async function wikiFetch(config2,
|
|
8621
|
-
const url = `${config2.baseUrl}/api/v1${
|
|
8649
|
+
async function wikiFetch(config2, path56, method = "GET", body) {
|
|
8650
|
+
const url = `${config2.baseUrl}/api/v1${path56}`;
|
|
8622
8651
|
const headers = {
|
|
8623
8652
|
Authorization: `Bearer ${config2.apiKey}`,
|
|
8624
8653
|
"Content-Type": "application/json"
|
|
@@ -8651,7 +8680,7 @@ async function wikiFetch(config2, path55, method = "GET", body) {
|
|
|
8651
8680
|
}
|
|
8652
8681
|
}
|
|
8653
8682
|
if (!response.ok) {
|
|
8654
|
-
throw new Error(`Wiki API ${method} ${
|
|
8683
|
+
throw new Error(`Wiki API ${method} ${path56}: ${response.status} ${response.statusText}`);
|
|
8655
8684
|
}
|
|
8656
8685
|
return response.json();
|
|
8657
8686
|
} finally {
|
|
@@ -9408,7 +9437,7 @@ function readQueue() {
|
|
|
9408
9437
|
function writeQueue(queue) {
|
|
9409
9438
|
ensureDir();
|
|
9410
9439
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
9411
|
-
writeFileSync9(tmp, JSON.stringify(queue, null, 2));
|
|
9440
|
+
writeFileSync9(tmp, JSON.stringify(queue, null, 2), { mode: 384 });
|
|
9412
9441
|
renameSync4(tmp, QUEUE_PATH);
|
|
9413
9442
|
}
|
|
9414
9443
|
function queueIntercom(targetSession, reason) {
|
|
@@ -12786,12 +12815,14 @@ On EVERY new conversation, before doing anything else:
|
|
|
12786
12815
|
1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
|
|
12787
12816
|
2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
|
|
12788
12817
|
3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
|
|
12789
|
-
4. **
|
|
12818
|
+
4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
|
|
12819
|
+
5. **Present the brief**: Give the founder a concise status report:
|
|
12790
12820
|
- What's active and progressing
|
|
12791
12821
|
- What's blocked and needs attention
|
|
12792
12822
|
- What decisions are pending
|
|
12823
|
+
- Available bug fixes (from step 4, if any)
|
|
12793
12824
|
- What you recommend doing next
|
|
12794
|
-
|
|
12825
|
+
6. Then ask: "What's the priority?"
|
|
12795
12826
|
|
|
12796
12827
|
If this is your FIRST ever conversation (few or no prior memories):
|
|
12797
12828
|
- Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
|
|
@@ -12811,6 +12842,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
12811
12842
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
12812
12843
|
- **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
|
|
12813
12844
|
- **send_message** \u2014 direct intercom to employees
|
|
12845
|
+
- **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
|
|
12846
|
+
- **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
|
|
12814
12847
|
${PLAN_MODE_COMPAT}
|
|
12815
12848
|
## Completion Workflow
|
|
12816
12849
|
|
|
@@ -13974,6 +14007,21 @@ var init_crdt_sync = __esm({
|
|
|
13974
14007
|
}
|
|
13975
14008
|
});
|
|
13976
14009
|
|
|
14010
|
+
// src/lib/pg-ssl.ts
|
|
14011
|
+
var pg_ssl_exports = {};
|
|
14012
|
+
__export(pg_ssl_exports, {
|
|
14013
|
+
pgSslConfig: () => pgSslConfig
|
|
14014
|
+
});
|
|
14015
|
+
function pgSslConfig() {
|
|
14016
|
+
if (process.env.EXE_DB_SSL_DISABLED === "true") return {};
|
|
14017
|
+
return { ssl: { rejectUnauthorized: process.env.EXE_DB_SSL_ALLOW_SELFSIGNED !== "true" } };
|
|
14018
|
+
}
|
|
14019
|
+
var init_pg_ssl = __esm({
|
|
14020
|
+
"src/lib/pg-ssl.ts"() {
|
|
14021
|
+
"use strict";
|
|
14022
|
+
}
|
|
14023
|
+
});
|
|
14024
|
+
|
|
13977
14025
|
// src/lib/tmux-status.ts
|
|
13978
14026
|
import { execSync as execSync13 } from "child_process";
|
|
13979
14027
|
function inTmux() {
|
|
@@ -14208,7 +14256,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
14208
14256
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
14209
14257
|
import { spawn as spawn4 } from "child_process";
|
|
14210
14258
|
import { existsSync as existsSync41, openSync as openSync4, mkdirSync as mkdirSync23, closeSync as closeSync4, readFileSync as readFileSync36 } from "fs";
|
|
14211
|
-
import
|
|
14259
|
+
import path55 from "path";
|
|
14212
14260
|
import os20 from "os";
|
|
14213
14261
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
14214
14262
|
|
|
@@ -18596,12 +18644,12 @@ function registerExportGraph(server2) {
|
|
|
18596
18644
|
}
|
|
18597
18645
|
const html = await exportGraphHTML(client);
|
|
18598
18646
|
const fs = await import("fs");
|
|
18599
|
-
const
|
|
18647
|
+
const path56 = await import("path");
|
|
18600
18648
|
const os21 = await import("os");
|
|
18601
|
-
const outDir =
|
|
18649
|
+
const outDir = path56.join(os21.homedir(), ".exe-os", "exports");
|
|
18602
18650
|
fs.mkdirSync(outDir, { recursive: true });
|
|
18603
18651
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
18604
|
-
const filePath =
|
|
18652
|
+
const filePath = path56.join(outDir, `graph-${timestamp}.html`);
|
|
18605
18653
|
fs.writeFileSync(filePath, html, "utf-8");
|
|
18606
18654
|
return {
|
|
18607
18655
|
content: [
|
|
@@ -19918,6 +19966,130 @@ import { existsSync as existsSync24, readFileSync as readFileSync20 } from "fs";
|
|
|
19918
19966
|
import path30 from "path";
|
|
19919
19967
|
import { homedir as homedir4 } from "os";
|
|
19920
19968
|
import { z as z54 } from "zod";
|
|
19969
|
+
|
|
19970
|
+
// src/mcp/license-gate.ts
|
|
19971
|
+
init_license();
|
|
19972
|
+
var _cachedLicense = null;
|
|
19973
|
+
var _hasLicenseKey = false;
|
|
19974
|
+
async function initLicenseGate() {
|
|
19975
|
+
const key = loadLicense();
|
|
19976
|
+
_hasLicenseKey = key !== null && key.length > 0;
|
|
19977
|
+
if (_hasLicenseKey) {
|
|
19978
|
+
try {
|
|
19979
|
+
_cachedLicense = await checkLicense();
|
|
19980
|
+
} catch {
|
|
19981
|
+
_cachedLicense = null;
|
|
19982
|
+
}
|
|
19983
|
+
return { license: _cachedLicense, hasKey: true };
|
|
19984
|
+
}
|
|
19985
|
+
return { license: null, hasKey: false };
|
|
19986
|
+
}
|
|
19987
|
+
function getCachedLicenseGate() {
|
|
19988
|
+
return {
|
|
19989
|
+
license: _cachedLicense,
|
|
19990
|
+
hasKey: _hasLicenseKey
|
|
19991
|
+
};
|
|
19992
|
+
}
|
|
19993
|
+
|
|
19994
|
+
// src/mcp/tool-telemetry.ts
|
|
19995
|
+
var _toolCalls = /* @__PURE__ */ new Map();
|
|
19996
|
+
var _sessionStartedAt = Date.now();
|
|
19997
|
+
var _flushTimer2 = null;
|
|
19998
|
+
function recordCall(toolName, action, isError) {
|
|
19999
|
+
let record = _toolCalls.get(toolName);
|
|
20000
|
+
if (!record) {
|
|
20001
|
+
record = { count: 0, actions: /* @__PURE__ */ new Map(), lastCalledAt: 0, errors: 0 };
|
|
20002
|
+
_toolCalls.set(toolName, record);
|
|
20003
|
+
}
|
|
20004
|
+
record.count++;
|
|
20005
|
+
record.lastCalledAt = Date.now();
|
|
20006
|
+
if (isError) record.errors++;
|
|
20007
|
+
if (action) {
|
|
20008
|
+
record.actions.set(action, (record.actions.get(action) ?? 0) + 1);
|
|
20009
|
+
}
|
|
20010
|
+
}
|
|
20011
|
+
function wrapServerWithTelemetry(server2) {
|
|
20012
|
+
const originalRegisterTool = server2.registerTool.bind(server2);
|
|
20013
|
+
server2.registerTool = (name, config2, handler) => {
|
|
20014
|
+
const wrappedHandler = async (input, extra) => {
|
|
20015
|
+
const action = input.action;
|
|
20016
|
+
try {
|
|
20017
|
+
const result3 = await handler(input, extra);
|
|
20018
|
+
const isError = result3?.isError === true;
|
|
20019
|
+
recordCall(name, action, isError);
|
|
20020
|
+
return result3;
|
|
20021
|
+
} catch (err) {
|
|
20022
|
+
recordCall(name, action, true);
|
|
20023
|
+
throw err;
|
|
20024
|
+
}
|
|
20025
|
+
};
|
|
20026
|
+
return originalRegisterTool(name, config2, wrappedHandler);
|
|
20027
|
+
};
|
|
20028
|
+
return server2;
|
|
20029
|
+
}
|
|
20030
|
+
function getToolUsageStats() {
|
|
20031
|
+
let totalCalls = 0;
|
|
20032
|
+
let totalErrors = 0;
|
|
20033
|
+
const tools = {};
|
|
20034
|
+
for (const [name, record] of _toolCalls) {
|
|
20035
|
+
totalCalls += record.count;
|
|
20036
|
+
totalErrors += record.errors;
|
|
20037
|
+
const entry = {
|
|
20038
|
+
calls: record.count,
|
|
20039
|
+
errors: record.errors,
|
|
20040
|
+
lastCalledAt: new Date(record.lastCalledAt).toISOString()
|
|
20041
|
+
};
|
|
20042
|
+
if (record.actions.size > 0) {
|
|
20043
|
+
entry.actions = Object.fromEntries(record.actions);
|
|
20044
|
+
}
|
|
20045
|
+
tools[name] = entry;
|
|
20046
|
+
}
|
|
20047
|
+
return {
|
|
20048
|
+
sessionStartedAt: new Date(_sessionStartedAt).toISOString(),
|
|
20049
|
+
uptimeMs: Date.now() - _sessionStartedAt,
|
|
20050
|
+
totalCalls,
|
|
20051
|
+
totalErrors,
|
|
20052
|
+
tools
|
|
20053
|
+
};
|
|
20054
|
+
}
|
|
20055
|
+
var FLUSH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
20056
|
+
var _lastFlushedAt = 0;
|
|
20057
|
+
async function flushToMemory() {
|
|
20058
|
+
const stats = getToolUsageStats();
|
|
20059
|
+
if (stats.totalCalls === 0) return;
|
|
20060
|
+
if (stats.totalCalls === _lastFlushedAt) return;
|
|
20061
|
+
try {
|
|
20062
|
+
const { getClient: getClient2, isInitialized: isInitialized2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
20063
|
+
if (!isInitialized2()) return;
|
|
20064
|
+
const client = getClient2();
|
|
20065
|
+
const toolSummary = Object.entries(stats.tools).sort((a, b) => b[1].calls - a[1].calls).map(([name, t]) => {
|
|
20066
|
+
const actionStr = t.actions ? ` (${Object.entries(t.actions).sort((a, b) => b[1] - a[1]).map(([a, c]) => `${a}:${c}`).join(", ")})` : "";
|
|
20067
|
+
return `${name}: ${t.calls} calls${t.errors > 0 ? ` (${t.errors} errors)` : ""}${actionStr}`;
|
|
20068
|
+
}).join("\n");
|
|
20069
|
+
const raw_text = `Tool usage since ${stats.sessionStartedAt} (${Math.round(stats.uptimeMs / 6e4)}min):
|
|
20070
|
+
Total: ${stats.totalCalls} calls, ${stats.totalErrors} errors
|
|
20071
|
+
|
|
20072
|
+
${toolSummary}`;
|
|
20073
|
+
await client.execute({
|
|
20074
|
+
sql: `INSERT INTO memories (id, agent_id, raw_text, memory_type, project_name, importance, created_at, updated_at)
|
|
20075
|
+
VALUES (?, 'system', ?, 'telemetry', 'exe-os', 2, datetime('now'), datetime('now'))`,
|
|
20076
|
+
args: [
|
|
20077
|
+
`telemetry-tools-${Date.now()}`,
|
|
20078
|
+
raw_text
|
|
20079
|
+
]
|
|
20080
|
+
});
|
|
20081
|
+
_lastFlushedAt = stats.totalCalls;
|
|
20082
|
+
} catch {
|
|
20083
|
+
}
|
|
20084
|
+
}
|
|
20085
|
+
function startToolTelemetryFlush() {
|
|
20086
|
+
if (_flushTimer2) return;
|
|
20087
|
+
_sessionStartedAt = Date.now();
|
|
20088
|
+
_flushTimer2 = setInterval(() => void flushToMemory(), FLUSH_INTERVAL_MS);
|
|
20089
|
+
_flushTimer2.unref();
|
|
20090
|
+
}
|
|
20091
|
+
|
|
20092
|
+
// src/mcp/tools/mcp-ping.ts
|
|
19921
20093
|
var PID_PATH3 = path30.join(homedir4(), ".exe-os", "exed.pid");
|
|
19922
20094
|
function isDaemonAlive2() {
|
|
19923
20095
|
try {
|
|
@@ -19944,6 +20116,7 @@ function registerMcpPing(server2) {
|
|
|
19944
20116
|
const daemon = isDaemonAlive2();
|
|
19945
20117
|
const summary = summarizeMcpTransport();
|
|
19946
20118
|
writeMcpTransportSummary();
|
|
20119
|
+
const gate = getCachedLicenseGate();
|
|
19947
20120
|
const status2 = daemon.alive ? "ok" : "degraded";
|
|
19948
20121
|
return {
|
|
19949
20122
|
content: [
|
|
@@ -19952,8 +20125,14 @@ function registerMcpPing(server2) {
|
|
|
19952
20125
|
text: JSON.stringify({
|
|
19953
20126
|
status: status2,
|
|
19954
20127
|
daemon,
|
|
20128
|
+
licenseGate: {
|
|
20129
|
+
hasKey: gate.hasKey,
|
|
20130
|
+
plan: gate.license?.plan ?? "none",
|
|
20131
|
+
valid: gate.license?.valid ?? false
|
|
20132
|
+
},
|
|
20133
|
+
toolUsage: getToolUsageStats(),
|
|
19955
20134
|
mcpTransport: summary,
|
|
19956
|
-
remediation: daemon.alive ? "MCP transport probe reached the server. If tools still fail, reconnect the MCP client." : "Daemon is offline. Restart exe-os/exed; do not bypass MCP or access the DB directly."
|
|
20135
|
+
remediation: daemon.alive ? gate.hasKey ? "MCP transport probe reached the server. If tools still fail, reconnect the MCP client." : "No license key found. Run: echo 'exe_sk_YOUR_KEY' > ~/.exe-os/license.key then reconnect MCP." : "Daemon is offline. Restart exe-os/exed; do not bypass MCP or access the DB directly."
|
|
19957
20136
|
}, null, 2)
|
|
19958
20137
|
}
|
|
19959
20138
|
]
|
|
@@ -20159,6 +20338,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
20159
20338
|
function isMainModule(importMetaUrl) {
|
|
20160
20339
|
if (process.argv[1] == null) return false;
|
|
20161
20340
|
if (process.argv[1].includes("mcp/server")) return false;
|
|
20341
|
+
if (process.argv[1].includes("exe-daemon")) return false;
|
|
20162
20342
|
try {
|
|
20163
20343
|
const scriptPath = realpathSync(process.argv[1]);
|
|
20164
20344
|
const modulePath = realpathSync(fileURLToPath4(importMetaUrl));
|
|
@@ -21575,7 +21755,8 @@ function loadPgClient() {
|
|
|
21575
21755
|
return new Ctor();
|
|
21576
21756
|
}
|
|
21577
21757
|
const { Pool } = await import("pg");
|
|
21578
|
-
const
|
|
21758
|
+
const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
|
|
21759
|
+
const pool = new Pool({ connectionString: process.env.DATABASE_URL, ...pgSslConfig2() });
|
|
21579
21760
|
return {
|
|
21580
21761
|
async $queryRawUnsafe(query, ...values) {
|
|
21581
21762
|
const result3 = await pool.query(query, values);
|
|
@@ -22104,6 +22285,17 @@ async function cloudSync(config2) {
|
|
|
22104
22285
|
} catch (err) {
|
|
22105
22286
|
logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
|
|
22106
22287
|
}
|
|
22288
|
+
let codeContextResult = { pushed: 0, pulled: 0 };
|
|
22289
|
+
try {
|
|
22290
|
+
codeContextResult.pushed = await cloudPushCodeContext(config2);
|
|
22291
|
+
} catch (err) {
|
|
22292
|
+
logError(`[cloud-sync] Code context push: ${err instanceof Error ? err.message : String(err)}`);
|
|
22293
|
+
}
|
|
22294
|
+
try {
|
|
22295
|
+
codeContextResult.pulled = await cloudPullCodeContext(config2);
|
|
22296
|
+
} catch (err) {
|
|
22297
|
+
logError(`[cloud-sync] Code context pull: ${err instanceof Error ? err.message : String(err)}`);
|
|
22298
|
+
}
|
|
22107
22299
|
return {
|
|
22108
22300
|
pushed,
|
|
22109
22301
|
pulled,
|
|
@@ -22113,7 +22305,8 @@ async function cloudSync(config2) {
|
|
|
22113
22305
|
tasks: tasksResult,
|
|
22114
22306
|
conversations: conversationsResult,
|
|
22115
22307
|
documents: documentsResult,
|
|
22116
|
-
roster: rosterResult
|
|
22308
|
+
roster: rosterResult,
|
|
22309
|
+
codeContext: codeContextResult
|
|
22117
22310
|
};
|
|
22118
22311
|
}
|
|
22119
22312
|
var ROSTER_DELETIONS_PATH = path37.join(EXE_AI_DIR, "roster-deletions.json");
|
|
@@ -22741,6 +22934,99 @@ async function cloudPullDocuments(config2) {
|
|
|
22741
22934
|
}
|
|
22742
22935
|
return { pulled };
|
|
22743
22936
|
}
|
|
22937
|
+
var CODE_CONTEXT_DIR = path37.join(EXE_AI_DIR, "code-context");
|
|
22938
|
+
async function cloudPushCodeContext(config2) {
|
|
22939
|
+
assertSecureEndpoint(config2.endpoint);
|
|
22940
|
+
if (!existsSync32(CODE_CONTEXT_DIR)) return 0;
|
|
22941
|
+
const files = readdirSync11(CODE_CONTEXT_DIR).filter(
|
|
22942
|
+
(f) => f.endsWith(".json") && !f.endsWith(".vectors.json") && !f.startsWith(".")
|
|
22943
|
+
);
|
|
22944
|
+
if (files.length === 0) return 0;
|
|
22945
|
+
const metaPath = path37.join(CODE_CONTEXT_DIR, ".sync-meta.json");
|
|
22946
|
+
let syncMeta = {};
|
|
22947
|
+
if (existsSync32(metaPath)) {
|
|
22948
|
+
try {
|
|
22949
|
+
syncMeta = JSON.parse(readFileSync25(metaPath, "utf-8"));
|
|
22950
|
+
} catch {
|
|
22951
|
+
}
|
|
22952
|
+
}
|
|
22953
|
+
let pushed = 0;
|
|
22954
|
+
for (const file of files) {
|
|
22955
|
+
const filePath = path37.join(CODE_CONTEXT_DIR, file);
|
|
22956
|
+
try {
|
|
22957
|
+
const stat = statSync7(filePath);
|
|
22958
|
+
const lastPushed = syncMeta[file] ?? 0;
|
|
22959
|
+
if (stat.mtimeMs <= lastPushed) continue;
|
|
22960
|
+
const content = readFileSync25(filePath, "utf-8");
|
|
22961
|
+
const header = content.substring(0, 300);
|
|
22962
|
+
if (header.includes("/tmp") || header.includes("/var/folders") || header.includes(".worktrees/")) continue;
|
|
22963
|
+
const compressed = compress(Buffer.from(content, "utf8"));
|
|
22964
|
+
const encrypted = encryptSyncBlob(compressed);
|
|
22965
|
+
const resp = await fetchWithRetry(`${config2.endpoint}/sync/push-code-context`, {
|
|
22966
|
+
method: "POST",
|
|
22967
|
+
headers: {
|
|
22968
|
+
Authorization: `Bearer ${config2.apiKey}`,
|
|
22969
|
+
"Content-Type": "application/json",
|
|
22970
|
+
"X-Device-Id": loadDeviceId()
|
|
22971
|
+
},
|
|
22972
|
+
body: JSON.stringify({ key: file, blob: encrypted })
|
|
22973
|
+
});
|
|
22974
|
+
if (resp.ok) {
|
|
22975
|
+
syncMeta[file] = stat.mtimeMs;
|
|
22976
|
+
pushed++;
|
|
22977
|
+
}
|
|
22978
|
+
} catch {
|
|
22979
|
+
}
|
|
22980
|
+
}
|
|
22981
|
+
if (pushed > 0) {
|
|
22982
|
+
try {
|
|
22983
|
+
writeFileSync18(metaPath, JSON.stringify(syncMeta));
|
|
22984
|
+
} catch {
|
|
22985
|
+
}
|
|
22986
|
+
}
|
|
22987
|
+
return pushed;
|
|
22988
|
+
}
|
|
22989
|
+
async function cloudPullCodeContext(config2) {
|
|
22990
|
+
assertSecureEndpoint(config2.endpoint);
|
|
22991
|
+
try {
|
|
22992
|
+
const resp = await fetchWithRetry(`${config2.endpoint}/sync/pull-code-context`, {
|
|
22993
|
+
method: "GET",
|
|
22994
|
+
headers: {
|
|
22995
|
+
Authorization: `Bearer ${config2.apiKey}`,
|
|
22996
|
+
"X-Device-Id": loadDeviceId()
|
|
22997
|
+
}
|
|
22998
|
+
});
|
|
22999
|
+
if (!resp.ok) return 0;
|
|
23000
|
+
const data = await resp.json();
|
|
23001
|
+
if (!data.indexes || data.indexes.length === 0) return 0;
|
|
23002
|
+
mkdirSync16(CODE_CONTEXT_DIR, { recursive: true });
|
|
23003
|
+
let pulled = 0;
|
|
23004
|
+
for (const { key, blob } of data.indexes) {
|
|
23005
|
+
try {
|
|
23006
|
+
if (key.endsWith(".vectors.json")) continue;
|
|
23007
|
+
const localPath = path37.join(CODE_CONTEXT_DIR, key);
|
|
23008
|
+
const compressed = decryptSyncBlob(blob);
|
|
23009
|
+
const content = decompress(compressed).toString("utf8");
|
|
23010
|
+
if (!existsSync32(localPath)) {
|
|
23011
|
+
writeFileSync18(localPath, content, "utf-8");
|
|
23012
|
+
pulled++;
|
|
23013
|
+
} else {
|
|
23014
|
+
const localContent = readFileSync25(localPath, "utf-8");
|
|
23015
|
+
if (localContent.length !== content.length) {
|
|
23016
|
+
writeFileSync18(localPath, content, "utf-8");
|
|
23017
|
+
pulled++;
|
|
23018
|
+
}
|
|
23019
|
+
}
|
|
23020
|
+
} catch {
|
|
23021
|
+
}
|
|
23022
|
+
}
|
|
23023
|
+
return pulled;
|
|
23024
|
+
} catch (err) {
|
|
23025
|
+
process.stderr.write(`[cloud-sync] Code context pull failed: ${err instanceof Error ? err.message : String(err)}
|
|
23026
|
+
`);
|
|
23027
|
+
return 0;
|
|
23028
|
+
}
|
|
23029
|
+
}
|
|
22744
23030
|
|
|
22745
23031
|
// src/mcp/tools/cloud-sync.ts
|
|
22746
23032
|
init_config();
|
|
@@ -23580,9 +23866,9 @@ var HostingerApiClient = class {
|
|
|
23580
23866
|
}
|
|
23581
23867
|
this.lastRequestTime = Date.now();
|
|
23582
23868
|
}
|
|
23583
|
-
async request(method,
|
|
23869
|
+
async request(method, path56, body) {
|
|
23584
23870
|
await this.rateLimit();
|
|
23585
|
-
const url = `${this.baseUrl}${
|
|
23871
|
+
const url = `${this.baseUrl}${path56}`;
|
|
23586
23872
|
const headers = {
|
|
23587
23873
|
Authorization: `Bearer ${this.apiKey}`,
|
|
23588
23874
|
"Content-Type": "application/json",
|
|
@@ -23655,8 +23941,8 @@ async function requestCloudflare(cfApiToken, zoneId, options) {
|
|
|
23655
23941
|
}
|
|
23656
23942
|
return envelope.result;
|
|
23657
23943
|
}
|
|
23658
|
-
function buildUrl(zoneId,
|
|
23659
|
-
const normalizedPath =
|
|
23944
|
+
function buildUrl(zoneId, path56 = "/dns_records", query) {
|
|
23945
|
+
const normalizedPath = path56.startsWith("/") ? path56 : `/${path56}`;
|
|
23660
23946
|
const url = new URL(
|
|
23661
23947
|
`${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
|
|
23662
23948
|
);
|
|
@@ -26899,6 +27185,22 @@ async function getExeDbReadClient() {
|
|
|
26899
27185
|
}
|
|
26900
27186
|
if (!prismaPromise3) {
|
|
26901
27187
|
prismaPromise3 = (async () => {
|
|
27188
|
+
const readonlyUrl = process.env.MCP_READONLY_DATABASE_URL;
|
|
27189
|
+
if (readonlyUrl) {
|
|
27190
|
+
const { Pool } = await import("pg");
|
|
27191
|
+
const { pgSslConfig: pgSslConfig2 } = await Promise.resolve().then(() => (init_pg_ssl(), pg_ssl_exports));
|
|
27192
|
+
const pool = new Pool({ connectionString: readonlyUrl, ...pgSslConfig2() });
|
|
27193
|
+
return {
|
|
27194
|
+
async $queryRawUnsafe(query, ...values) {
|
|
27195
|
+
const result3 = await pool.query(query, values);
|
|
27196
|
+
return result3.rows;
|
|
27197
|
+
},
|
|
27198
|
+
async $disconnect() {
|
|
27199
|
+
await pool.end();
|
|
27200
|
+
}
|
|
27201
|
+
};
|
|
27202
|
+
}
|
|
27203
|
+
console.warn("[exe-db-read] WARNING: MCP_READONLY_DATABASE_URL not set \u2014 falling back to admin role");
|
|
26902
27204
|
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
26903
27205
|
if (explicitPath) {
|
|
26904
27206
|
const mod2 = await import(pathToFileURL5(explicitPath).href);
|
|
@@ -27367,14 +27669,206 @@ Upstream status: ${upstreamStatus}`
|
|
|
27367
27669
|
);
|
|
27368
27670
|
}
|
|
27369
27671
|
|
|
27370
|
-
// src/mcp/tools/
|
|
27672
|
+
// src/mcp/tools/create-feature-request.ts
|
|
27673
|
+
init_embedder();
|
|
27674
|
+
init_active_agent();
|
|
27675
|
+
init_config();
|
|
27676
|
+
init_license();
|
|
27677
|
+
init_store();
|
|
27371
27678
|
import { z as z89 } from "zod";
|
|
27679
|
+
import crypto19 from "crypto";
|
|
27680
|
+
import { mkdir as mkdir7, writeFile as writeFile8 } from "fs/promises";
|
|
27681
|
+
import path50 from "path";
|
|
27682
|
+
var CATEGORY = z89.enum([
|
|
27683
|
+
"upstream_feature",
|
|
27684
|
+
"local_customization",
|
|
27685
|
+
"integration",
|
|
27686
|
+
"unclear"
|
|
27687
|
+
]);
|
|
27688
|
+
var PRIORITY = z89.enum(["p0", "p1", "p2", "p3"]);
|
|
27689
|
+
function slugify3(input) {
|
|
27690
|
+
return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80) || "feature-request";
|
|
27691
|
+
}
|
|
27692
|
+
function section2(title, body) {
|
|
27693
|
+
return `## ${title}
|
|
27694
|
+
|
|
27695
|
+
${body?.trim() || "Not provided"}`;
|
|
27696
|
+
}
|
|
27697
|
+
function buildMarkdown2(input) {
|
|
27698
|
+
return [
|
|
27699
|
+
`# Feature Request \u2014 ${input.title}`,
|
|
27700
|
+
"",
|
|
27701
|
+
`id: ${input.id}`,
|
|
27702
|
+
`category: ${input.category}`,
|
|
27703
|
+
`priority: ${input.priority}`,
|
|
27704
|
+
`filed_by: ${input.agentId} (${input.agentRole})`,
|
|
27705
|
+
`package_version: ${input.packageVersion}`,
|
|
27706
|
+
`project: ${input.projectName ?? "unknown"}`,
|
|
27707
|
+
`created_at: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
27708
|
+
"",
|
|
27709
|
+
section2("Description", input.description),
|
|
27710
|
+
section2("Use Case", input.useCase),
|
|
27711
|
+
section2("Current Workaround", input.currentWorkaround),
|
|
27712
|
+
section2("Proposed Solution", input.proposedSolution),
|
|
27713
|
+
section2("Business Impact", input.businessImpact)
|
|
27714
|
+
].join("\n");
|
|
27715
|
+
}
|
|
27716
|
+
async function maybeSendUpstream2(payload) {
|
|
27717
|
+
const config2 = await loadConfig();
|
|
27718
|
+
const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
|
|
27719
|
+
const endpoint2 = config2.support?.featureRequestEndpoint || process.env.EXE_FEATURE_REQUEST_ENDPOINT || (routerUrl ? `${routerUrl}/v1/support/feature-requests` : "https://askexe.com/v1/support/feature-requests");
|
|
27720
|
+
const token = config2.support?.featureRequestToken || process.env.EXE_FEATURE_REQUEST_TOKEN;
|
|
27721
|
+
const licenseKey = loadLicense() || process.env.EXE_LICENSE_KEY || config2.cloud?.apiKey;
|
|
27722
|
+
const licenseToken = readCachedLicenseToken();
|
|
27723
|
+
if (!endpoint2) {
|
|
27724
|
+
return "not_configured";
|
|
27725
|
+
}
|
|
27726
|
+
try {
|
|
27727
|
+
const parsed = new URL(endpoint2);
|
|
27728
|
+
if (parsed.protocol !== "https:" && !["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)) {
|
|
27729
|
+
return "failed: insecure endpoint rejected";
|
|
27730
|
+
}
|
|
27731
|
+
const response = await fetch(parsed, {
|
|
27732
|
+
method: "POST",
|
|
27733
|
+
headers: {
|
|
27734
|
+
"content-type": "application/json",
|
|
27735
|
+
...token ? { authorization: `Bearer ${token}` } : {},
|
|
27736
|
+
...licenseKey ? { "x-exe-license-key": licenseKey } : {},
|
|
27737
|
+
...licenseToken ? { "x-exe-license-token": licenseToken } : {}
|
|
27738
|
+
},
|
|
27739
|
+
body: JSON.stringify(payload),
|
|
27740
|
+
signal: AbortSignal.timeout(1e4)
|
|
27741
|
+
});
|
|
27742
|
+
if (!response.ok) return `failed: HTTP ${response.status}`;
|
|
27743
|
+
return "sent";
|
|
27744
|
+
} catch (err) {
|
|
27745
|
+
return `failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
27746
|
+
}
|
|
27747
|
+
}
|
|
27748
|
+
function registerCreateFeatureRequest(server2) {
|
|
27749
|
+
server2.registerTool(
|
|
27750
|
+
"create_feature_request",
|
|
27751
|
+
{
|
|
27752
|
+
title: "Create Feature Request",
|
|
27753
|
+
description: "File a feature request for exe-os: upstream_feature (requires platform changes), local_customization (configurable in customer layers), integration (third-party), or unclear. Writes a local report, stores memory, and optionally sends to AskExe.",
|
|
27754
|
+
inputSchema: {
|
|
27755
|
+
title: z89.string().min(3).describe("Short descriptive title"),
|
|
27756
|
+
category: CATEGORY.describe(
|
|
27757
|
+
"upstream_feature = platform capability change; local_customization = configurable in customer layers; integration = third-party connector; unclear = needs product triage"
|
|
27758
|
+
),
|
|
27759
|
+
priority: PRIORITY.default("p2").describe("p0 critical \u2192 p3 low"),
|
|
27760
|
+
description: z89.string().min(10).describe("What capability is needed and why"),
|
|
27761
|
+
use_case: z89.string().optional().describe("Concrete scenario where this feature would be used"),
|
|
27762
|
+
current_workaround: z89.string().optional().describe("How the need is currently addressed, if at all"),
|
|
27763
|
+
proposed_solution: z89.string().optional().describe("Suggested implementation approach"),
|
|
27764
|
+
business_impact: z89.string().optional().describe("Impact on business/workflow if this ships"),
|
|
27765
|
+
package_version: z89.string().optional().describe("Installed @askexenow/exe-os version"),
|
|
27766
|
+
project_name: z89.string().optional().describe("Project/customer context"),
|
|
27767
|
+
send_upstream: z89.boolean().default(true).describe("Attempt to POST to configured AskExe support endpoint")
|
|
27768
|
+
}
|
|
27769
|
+
},
|
|
27770
|
+
async ({
|
|
27771
|
+
title,
|
|
27772
|
+
category,
|
|
27773
|
+
priority,
|
|
27774
|
+
description,
|
|
27775
|
+
use_case,
|
|
27776
|
+
current_workaround,
|
|
27777
|
+
proposed_solution,
|
|
27778
|
+
business_impact,
|
|
27779
|
+
package_version,
|
|
27780
|
+
project_name,
|
|
27781
|
+
send_upstream
|
|
27782
|
+
}) => {
|
|
27783
|
+
const { agentId, agentRole } = getActiveAgent();
|
|
27784
|
+
const id = crypto19.randomUUID();
|
|
27785
|
+
const version = package_version ?? "unknown";
|
|
27786
|
+
const markdown = buildMarkdown2({
|
|
27787
|
+
id,
|
|
27788
|
+
title,
|
|
27789
|
+
category,
|
|
27790
|
+
priority,
|
|
27791
|
+
agentId,
|
|
27792
|
+
agentRole,
|
|
27793
|
+
packageVersion: version,
|
|
27794
|
+
description,
|
|
27795
|
+
useCase: use_case,
|
|
27796
|
+
currentWorkaround: current_workaround,
|
|
27797
|
+
proposedSolution: proposed_solution,
|
|
27798
|
+
businessImpact: business_impact,
|
|
27799
|
+
projectName: project_name
|
|
27800
|
+
});
|
|
27801
|
+
const outDir = path50.join(EXE_AI_DIR, "feature-requests");
|
|
27802
|
+
await mkdir7(outDir, { recursive: true });
|
|
27803
|
+
const reportPath = path50.join(outDir, `${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}-${slugify3(title)}-${id.slice(0, 8)}.md`);
|
|
27804
|
+
await writeFile8(reportPath, markdown, "utf-8");
|
|
27805
|
+
let vector = null;
|
|
27806
|
+
try {
|
|
27807
|
+
vector = await embed(markdown);
|
|
27808
|
+
} catch {
|
|
27809
|
+
vector = null;
|
|
27810
|
+
}
|
|
27811
|
+
await writeMemory({
|
|
27812
|
+
id,
|
|
27813
|
+
agent_id: agentId,
|
|
27814
|
+
agent_role: agentRole,
|
|
27815
|
+
session_id: process.env.SESSION_ID ?? "manual",
|
|
27816
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27817
|
+
tool_name: "create_feature_request",
|
|
27818
|
+
project_name: project_name ?? "support",
|
|
27819
|
+
has_error: false,
|
|
27820
|
+
raw_text: markdown,
|
|
27821
|
+
vector,
|
|
27822
|
+
source_path: reportPath,
|
|
27823
|
+
source_type: "feature_request",
|
|
27824
|
+
memory_type: "feature_request",
|
|
27825
|
+
tier: 1,
|
|
27826
|
+
importance: priority === "p0" ? 10 : priority === "p1" ? 9 : priority === "p2" ? 7 : 5,
|
|
27827
|
+
intent: "request",
|
|
27828
|
+
domain: "support",
|
|
27829
|
+
file_paths: null
|
|
27830
|
+
});
|
|
27831
|
+
await flushBatch();
|
|
27832
|
+
const upstreamStatus = send_upstream ? await maybeSendUpstream2({
|
|
27833
|
+
id,
|
|
27834
|
+
title,
|
|
27835
|
+
category,
|
|
27836
|
+
priority,
|
|
27837
|
+
description,
|
|
27838
|
+
use_case,
|
|
27839
|
+
current_workaround,
|
|
27840
|
+
proposed_solution,
|
|
27841
|
+
business_impact,
|
|
27842
|
+
package_version: version,
|
|
27843
|
+
project_name,
|
|
27844
|
+
agent_id: agentId,
|
|
27845
|
+
agent_role: agentRole
|
|
27846
|
+
}) : "skipped";
|
|
27847
|
+
return {
|
|
27848
|
+
content: [
|
|
27849
|
+
{
|
|
27850
|
+
type: "text",
|
|
27851
|
+
text: `Feature request created.
|
|
27852
|
+
ID: ${id}
|
|
27853
|
+
Category: ${category}
|
|
27854
|
+
Local report: ${reportPath}
|
|
27855
|
+
Memory stored: ${id}
|
|
27856
|
+
Upstream status: ${upstreamStatus}`
|
|
27857
|
+
}
|
|
27858
|
+
]
|
|
27859
|
+
};
|
|
27860
|
+
}
|
|
27861
|
+
);
|
|
27862
|
+
}
|
|
27863
|
+
|
|
27864
|
+
// src/mcp/tools/support.ts
|
|
27865
|
+
import { z as z90 } from "zod";
|
|
27372
27866
|
|
|
27373
27867
|
// src/bin/exe-support.ts
|
|
27374
27868
|
init_config();
|
|
27375
27869
|
init_license();
|
|
27376
27870
|
import { mkdirSync as mkdirSync21, readFileSync as readFileSync32, unlinkSync as unlinkSync12, writeFileSync as writeFileSync23 } from "fs";
|
|
27377
|
-
import
|
|
27871
|
+
import path51 from "path";
|
|
27378
27872
|
import { randomUUID as randomUUID9 } from "crypto";
|
|
27379
27873
|
var DEFAULT_BUG_ENDPOINT = "https://askexe.com/v1/support/bug-reports";
|
|
27380
27874
|
var DEFAULT_ADMIN_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
@@ -27495,8 +27989,8 @@ async function resolveEndpoints() {
|
|
|
27495
27989
|
return { bugEndpoint, healthEndpoint, adminEndpoint };
|
|
27496
27990
|
}
|
|
27497
27991
|
function checkLocalWrite() {
|
|
27498
|
-
const dir =
|
|
27499
|
-
const testPath =
|
|
27992
|
+
const dir = path51.join(EXE_AI_DIR, "bug-reports");
|
|
27993
|
+
const testPath = path51.join(dir, ".support-write-test");
|
|
27500
27994
|
try {
|
|
27501
27995
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27502
27996
|
writeFileSync23(testPath, "ok\n", { mode: 384 });
|
|
@@ -27512,10 +28006,10 @@ function checkLocalWrite() {
|
|
|
27512
28006
|
}
|
|
27513
28007
|
}
|
|
27514
28008
|
function writeLocalTestReport(id, project, version) {
|
|
27515
|
-
const dir =
|
|
28009
|
+
const dir = path51.join(EXE_AI_DIR, "bug-reports");
|
|
27516
28010
|
mkdirSync21(dir, { recursive: true, mode: 448 });
|
|
27517
28011
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
27518
|
-
const filePath =
|
|
28012
|
+
const filePath = path51.join(dir, `${date}-support-intake-test-${id.slice(0, 8)}.md`);
|
|
27519
28013
|
writeFileSync23(filePath, `# TEST \u2014 ${project} support intake
|
|
27520
28014
|
|
|
27521
28015
|
Report ID: ${id}
|
|
@@ -27557,15 +28051,15 @@ async function maybeCloseAdmin(id, adminEndpoint, version) {
|
|
|
27557
28051
|
}
|
|
27558
28052
|
}
|
|
27559
28053
|
function readPackageVersion2() {
|
|
27560
|
-
let dir =
|
|
28054
|
+
let dir = path51.dirname(new URL(import.meta.url).pathname);
|
|
27561
28055
|
for (let i = 0; i < 6; i++) {
|
|
27562
|
-
const pkg =
|
|
28056
|
+
const pkg = path51.join(dir, "package.json");
|
|
27563
28057
|
try {
|
|
27564
28058
|
const parsed = JSON.parse(readFileSync32(pkg, "utf8"));
|
|
27565
28059
|
if (parsed.version) return parsed.version;
|
|
27566
28060
|
} catch {
|
|
27567
28061
|
}
|
|
27568
|
-
dir =
|
|
28062
|
+
dir = path51.dirname(dir);
|
|
27569
28063
|
}
|
|
27570
28064
|
return "unknown";
|
|
27571
28065
|
}
|
|
@@ -27632,7 +28126,7 @@ function registerSupportTools(server2) {
|
|
|
27632
28126
|
title: "Support Test",
|
|
27633
28127
|
description: "End-to-end support intake smoke test. Files a clearly marked test report upstream and auto-closes it on AskExe machines with admin credentials.",
|
|
27634
28128
|
inputSchema: {
|
|
27635
|
-
project:
|
|
28129
|
+
project: z90.string().default("support-smoke").describe("Customer/project name, e.g. hygo")
|
|
27636
28130
|
}
|
|
27637
28131
|
},
|
|
27638
28132
|
async ({ project }) => {
|
|
@@ -27650,8 +28144,8 @@ function registerSupportTools(server2) {
|
|
|
27650
28144
|
title: "My Bug Reports",
|
|
27651
28145
|
description: "List bug reports you've filed, scoped to your license. Shows status (open/triaged/fixed/closed), severity, and fixed_version so you know when to update.",
|
|
27652
28146
|
inputSchema: {
|
|
27653
|
-
status:
|
|
27654
|
-
limit:
|
|
28147
|
+
status: z90.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("all").describe("Filter by status. Default: all"),
|
|
28148
|
+
limit: z90.number().min(1).max(50).default(25).describe("Max results")
|
|
27655
28149
|
}
|
|
27656
28150
|
},
|
|
27657
28151
|
async ({ status: status2, limit }) => {
|
|
@@ -27667,7 +28161,10 @@ function registerSupportTools(server2) {
|
|
|
27667
28161
|
endpoint2.searchParams.set("status", status2);
|
|
27668
28162
|
endpoint2.searchParams.set("limit", String(limit));
|
|
27669
28163
|
const headers = { "content-type": "application/json" };
|
|
27670
|
-
if (licenseKey)
|
|
28164
|
+
if (licenseKey) {
|
|
28165
|
+
headers["authorization"] = `Bearer ${licenseKey}`;
|
|
28166
|
+
headers["x-exe-license-key"] = licenseKey;
|
|
28167
|
+
}
|
|
27671
28168
|
if (licenseToken) headers["x-exe-license-token"] = licenseToken;
|
|
27672
28169
|
try {
|
|
27673
28170
|
const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
|
|
@@ -27707,12 +28204,79 @@ function registerSupportTools(server2) {
|
|
|
27707
28204
|
}
|
|
27708
28205
|
}
|
|
27709
28206
|
);
|
|
28207
|
+
server2.registerTool(
|
|
28208
|
+
"list_my_feature_requests",
|
|
28209
|
+
{
|
|
28210
|
+
title: "My Feature Requests",
|
|
28211
|
+
description: "List feature requests you've filed, scoped to your license. Shows status (open/planned/in_progress/shipped/closed), priority, and shipped_version so you know when to update.",
|
|
28212
|
+
inputSchema: {
|
|
28213
|
+
status: z90.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("all").describe("Filter by status. Default: all"),
|
|
28214
|
+
limit: z90.number().min(1).max(50).default(25).describe("Max results")
|
|
28215
|
+
}
|
|
28216
|
+
},
|
|
28217
|
+
async ({ status: status2, limit }) => {
|
|
28218
|
+
const licenseKey = loadLicense();
|
|
28219
|
+
const licenseToken = readCachedLicenseToken();
|
|
28220
|
+
if (!licenseKey && !licenseToken) {
|
|
28221
|
+
return {
|
|
28222
|
+
content: [{ type: "text", text: "No license key found. Run `exe-os setup` or `exe-os cloud setup` first." }],
|
|
28223
|
+
isError: true
|
|
28224
|
+
};
|
|
28225
|
+
}
|
|
28226
|
+
const endpoint2 = new URL("https://askexe.com/v1/support/my-feature-requests");
|
|
28227
|
+
endpoint2.searchParams.set("status", status2);
|
|
28228
|
+
endpoint2.searchParams.set("limit", String(limit));
|
|
28229
|
+
const headers = { "content-type": "application/json" };
|
|
28230
|
+
if (licenseKey) {
|
|
28231
|
+
headers["authorization"] = `Bearer ${licenseKey}`;
|
|
28232
|
+
headers["x-exe-license-key"] = licenseKey;
|
|
28233
|
+
}
|
|
28234
|
+
if (licenseToken) headers["x-exe-license-token"] = licenseToken;
|
|
28235
|
+
try {
|
|
28236
|
+
const res = await fetch(endpoint2.toString(), { method: "GET", headers, signal: AbortSignal.timeout(15e3) });
|
|
28237
|
+
if (!res.ok) {
|
|
28238
|
+
const body = await res.text().catch(() => "");
|
|
28239
|
+
return {
|
|
28240
|
+
content: [{ type: "text", text: `Failed to fetch feature requests: HTTP ${res.status}${body ? ` \u2014 ${body}` : ""}` }],
|
|
28241
|
+
isError: true
|
|
28242
|
+
};
|
|
28243
|
+
}
|
|
28244
|
+
const data = await res.json();
|
|
28245
|
+
if (data.count === 0) {
|
|
28246
|
+
return {
|
|
28247
|
+
content: [{ type: "text", text: `No feature requests found${status2 !== "all" ? ` with status '${status2}'` : ""}.` }],
|
|
28248
|
+
structuredContent: { items: [], count: 0 }
|
|
28249
|
+
};
|
|
28250
|
+
}
|
|
28251
|
+
const lines = [`Feature requests (${data.count}):`, ""];
|
|
28252
|
+
for (const r of data.items) {
|
|
28253
|
+
const priIcon = r.priority === "p0" ? "\u{1F534}" : r.priority === "p1" ? "\u{1F534}" : r.priority === "p2" ? "\u{1F7E0}" : "\u{1F7E2}";
|
|
28254
|
+
const statusIcon = r.status === "shipped" ? "\u2705" : r.status === "closed" ? "\u2611\uFE0F" : r.status === "planned" ? "\u{1F4CB}" : r.status === "in_progress" ? "\u{1F528}" : r.status === "wontdo" ? "\u26D4" : "\u{1F535}";
|
|
28255
|
+
lines.push(`${priIcon} ${r.priority.toUpperCase()} ${statusIcon} ${r.status} \u2014 ${r.title}`);
|
|
28256
|
+
lines.push(` ID: ${r.id} | Filed: ${r.created_at?.slice(0, 10) ?? "?"}`);
|
|
28257
|
+
if (r.shipped_version) lines.push(` \u{1F680} Shipped in: ${r.shipped_version} \u2014 run \`exe-os update\` to get this feature`);
|
|
28258
|
+
if (r.target_version) lines.push(` \u{1F3AF} Target: ${r.target_version}`);
|
|
28259
|
+
if (r.response_notes) lines.push(` \u{1F4DD} AskExe: ${r.response_notes}`);
|
|
28260
|
+
lines.push("");
|
|
28261
|
+
}
|
|
28262
|
+
return {
|
|
28263
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
28264
|
+
structuredContent: data
|
|
28265
|
+
};
|
|
28266
|
+
} catch (err) {
|
|
28267
|
+
return {
|
|
28268
|
+
content: [{ type: "text", text: `Error fetching feature requests: ${err instanceof Error ? err.message : String(err)}` }],
|
|
28269
|
+
isError: true
|
|
28270
|
+
};
|
|
28271
|
+
}
|
|
28272
|
+
}
|
|
28273
|
+
);
|
|
27710
28274
|
}
|
|
27711
28275
|
|
|
27712
28276
|
// src/mcp/tools/cli-parity.ts
|
|
27713
28277
|
import { execFile as execFile2 } from "child_process";
|
|
27714
28278
|
import { promisify as promisify2 } from "util";
|
|
27715
|
-
import { z as
|
|
28279
|
+
import { z as z91 } from "zod";
|
|
27716
28280
|
|
|
27717
28281
|
// src/bin/exe-status.ts
|
|
27718
28282
|
init_employees();
|
|
@@ -27773,22 +28337,22 @@ if (isMainModule(import.meta.url)) {
|
|
|
27773
28337
|
|
|
27774
28338
|
// src/bin/exe-healthcheck.ts
|
|
27775
28339
|
import { existsSync as existsSync39, readFileSync as readFileSync33, readdirSync as readdirSync14 } from "fs";
|
|
27776
|
-
import
|
|
28340
|
+
import path52 from "path";
|
|
27777
28341
|
import { execSync as execSync14 } from "child_process";
|
|
27778
28342
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
27779
28343
|
init_config();
|
|
27780
28344
|
function findPackageRoot2() {
|
|
27781
|
-
let dir =
|
|
27782
|
-
const { root } =
|
|
28345
|
+
let dir = path52.dirname(fileURLToPath6(import.meta.url));
|
|
28346
|
+
const { root } = path52.parse(dir);
|
|
27783
28347
|
while (dir !== root) {
|
|
27784
|
-
if (existsSync39(
|
|
27785
|
-
dir =
|
|
28348
|
+
if (existsSync39(path52.join(dir, "package.json"))) return dir;
|
|
28349
|
+
dir = path52.dirname(dir);
|
|
27786
28350
|
}
|
|
27787
28351
|
throw new Error("Cannot find package root");
|
|
27788
28352
|
}
|
|
27789
28353
|
function checkBuildIntegrity(pkgRoot) {
|
|
27790
28354
|
const results = [];
|
|
27791
|
-
const tsupConfig =
|
|
28355
|
+
const tsupConfig = path52.join(pkgRoot, "tsup.config.ts");
|
|
27792
28356
|
if (!existsSync39(tsupConfig)) {
|
|
27793
28357
|
return [{ name: "build/tsup-config", pass: false, detail: "tsup.config.ts not found" }];
|
|
27794
28358
|
}
|
|
@@ -27798,7 +28362,7 @@ function checkBuildIntegrity(pkgRoot) {
|
|
|
27798
28362
|
let total = 0;
|
|
27799
28363
|
for (const match of entryMatches) {
|
|
27800
28364
|
const outputKey = match[1];
|
|
27801
|
-
const expectedPath =
|
|
28365
|
+
const expectedPath = path52.join(pkgRoot, "dist", `${outputKey}.js`);
|
|
27802
28366
|
total++;
|
|
27803
28367
|
if (!existsSync39(expectedPath)) {
|
|
27804
28368
|
missing.push(`dist/${outputKey}.js`);
|
|
@@ -27822,7 +28386,7 @@ function checkBuildIntegrity(pkgRoot) {
|
|
|
27822
28386
|
}
|
|
27823
28387
|
function checkEmbedPipeline(pkgRoot) {
|
|
27824
28388
|
const results = [];
|
|
27825
|
-
const daemonPath =
|
|
28389
|
+
const daemonPath = path52.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
27826
28390
|
if (!existsSync39(daemonPath)) {
|
|
27827
28391
|
results.push({
|
|
27828
28392
|
name: "exed/daemon-exists",
|
|
@@ -27834,17 +28398,17 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27834
28398
|
results.push({ name: "exed/daemon-exists", pass: true, detail: "dist/lib/exe-daemon.js exists" });
|
|
27835
28399
|
const entryDirs = ["dist/hooks", "dist/bin", "dist/mcp"];
|
|
27836
28400
|
for (const dir of entryDirs) {
|
|
27837
|
-
const fullDir =
|
|
28401
|
+
const fullDir = path52.join(pkgRoot, dir);
|
|
27838
28402
|
if (!existsSync39(fullDir)) continue;
|
|
27839
28403
|
let walkDir = fullDir;
|
|
27840
|
-
const { root } =
|
|
28404
|
+
const { root } = path52.parse(walkDir);
|
|
27841
28405
|
let foundRoot = null;
|
|
27842
28406
|
while (walkDir !== root) {
|
|
27843
|
-
if (existsSync39(
|
|
28407
|
+
if (existsSync39(path52.join(walkDir, "package.json"))) {
|
|
27844
28408
|
foundRoot = walkDir;
|
|
27845
28409
|
break;
|
|
27846
28410
|
}
|
|
27847
|
-
walkDir =
|
|
28411
|
+
walkDir = path52.dirname(walkDir);
|
|
27848
28412
|
}
|
|
27849
28413
|
if (!foundRoot) {
|
|
27850
28414
|
results.push({
|
|
@@ -27854,7 +28418,7 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27854
28418
|
});
|
|
27855
28419
|
continue;
|
|
27856
28420
|
}
|
|
27857
|
-
const resolvedDaemon =
|
|
28421
|
+
const resolvedDaemon = path52.join(foundRoot, "dist", "lib", "exe-daemon.js");
|
|
27858
28422
|
const reachable = existsSync39(resolvedDaemon);
|
|
27859
28423
|
results.push({
|
|
27860
28424
|
name: `exed/reachable-from-${dir}`,
|
|
@@ -27866,7 +28430,7 @@ function checkEmbedPipeline(pkgRoot) {
|
|
|
27866
28430
|
}
|
|
27867
28431
|
function checkTaskSystem(pkgRoot) {
|
|
27868
28432
|
const results = [];
|
|
27869
|
-
const scannerPath =
|
|
28433
|
+
const scannerPath = path52.join(pkgRoot, "dist", "bin", "scan-tasks.js");
|
|
27870
28434
|
if (!existsSync39(scannerPath)) {
|
|
27871
28435
|
results.push({ name: "tasks/scanner", pass: false, detail: "scan-tasks.js not found" });
|
|
27872
28436
|
return results;
|
|
@@ -27889,7 +28453,7 @@ function checkTaskSystem(pkgRoot) {
|
|
|
27889
28453
|
}
|
|
27890
28454
|
function checkWorkerSpawning(pkgRoot) {
|
|
27891
28455
|
const results = [];
|
|
27892
|
-
const workerPath =
|
|
28456
|
+
const workerPath = path52.join(pkgRoot, "dist", "hooks", "ingest-worker.js");
|
|
27893
28457
|
if (!existsSync39(workerPath)) {
|
|
27894
28458
|
results.push({ name: "workers/ingest-worker", pass: false, detail: "ingest-worker.js not found" });
|
|
27895
28459
|
return results;
|
|
@@ -27904,14 +28468,14 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
27904
28468
|
detail: `Parse error: ${err instanceof Error ? err.message.slice(0, 200) : String(err)}`
|
|
27905
28469
|
});
|
|
27906
28470
|
}
|
|
27907
|
-
const hooksDir =
|
|
28471
|
+
const hooksDir = path52.join(pkgRoot, "dist", "hooks");
|
|
27908
28472
|
if (existsSync39(hooksDir)) {
|
|
27909
28473
|
const hookFiles = readdirSync14(hooksDir).filter((f) => f.endsWith(".js") && !f.endsWith(".js.map"));
|
|
27910
28474
|
let hooksPassed = 0;
|
|
27911
28475
|
const hooksFailed = [];
|
|
27912
28476
|
for (const hook of hookFiles) {
|
|
27913
28477
|
try {
|
|
27914
|
-
execSync14(`node --check "${
|
|
28478
|
+
execSync14(`node --check "${path52.join(hooksDir, hook)}" 2>&1`, { timeout: 1e4, encoding: "utf-8" });
|
|
27915
28479
|
hooksPassed++;
|
|
27916
28480
|
} catch {
|
|
27917
28481
|
hooksFailed.push(hook);
|
|
@@ -27935,8 +28499,8 @@ function checkWorkerSpawning(pkgRoot) {
|
|
|
27935
28499
|
}
|
|
27936
28500
|
function checkMcpTransport() {
|
|
27937
28501
|
const results = [];
|
|
27938
|
-
const pidPath =
|
|
27939
|
-
const tokenPath =
|
|
28502
|
+
const pidPath = path52.join(EXE_AI_DIR, "exed.pid");
|
|
28503
|
+
const tokenPath = path52.join(EXE_AI_DIR, "exed.token");
|
|
27940
28504
|
let daemonAlive = false;
|
|
27941
28505
|
if (existsSync39(pidPath)) {
|
|
27942
28506
|
try {
|
|
@@ -27983,7 +28547,7 @@ function checkMcpTransport() {
|
|
|
27983
28547
|
results.push({
|
|
27984
28548
|
name: "mcp/monitor-summary",
|
|
27985
28549
|
pass: true,
|
|
27986
|
-
detail: `Privacy-safe local monitor summary: ${
|
|
28550
|
+
detail: `Privacy-safe local monitor summary: ${path52.join(EXE_AI_DIR, "monitor", "mcp-transport-summary.json")}`
|
|
27987
28551
|
});
|
|
27988
28552
|
return results;
|
|
27989
28553
|
}
|
|
@@ -28041,7 +28605,7 @@ function checkClaudeCodeInstall() {
|
|
|
28041
28605
|
detail: "Failed to check claude binary path"
|
|
28042
28606
|
});
|
|
28043
28607
|
}
|
|
28044
|
-
const versionsDir =
|
|
28608
|
+
const versionsDir = path52.join(
|
|
28045
28609
|
process.env.HOME ?? process.env.USERPROFILE ?? "",
|
|
28046
28610
|
".local",
|
|
28047
28611
|
"share",
|
|
@@ -28131,7 +28695,7 @@ function runHealthCheck() {
|
|
|
28131
28695
|
const failed = results.filter((r) => !r.pass).length;
|
|
28132
28696
|
return { results, passed, failed };
|
|
28133
28697
|
}
|
|
28134
|
-
if (isMainModule(import.meta.url)) {
|
|
28698
|
+
if (isMainModule(import.meta.url) && (process.argv[1] ?? "").includes("exe-healthcheck")) {
|
|
28135
28699
|
const { results, passed, failed } = runHealthCheck();
|
|
28136
28700
|
console.log("\n exe-os Health Check\n");
|
|
28137
28701
|
for (const r of results) {
|
|
@@ -28148,9 +28712,9 @@ if (isMainModule(import.meta.url)) {
|
|
|
28148
28712
|
// src/lib/update-check.ts
|
|
28149
28713
|
import { execSync as execSync15 } from "child_process";
|
|
28150
28714
|
import { readFileSync as readFileSync34 } from "fs";
|
|
28151
|
-
import
|
|
28715
|
+
import path53 from "path";
|
|
28152
28716
|
function getLocalVersion(packageRoot) {
|
|
28153
|
-
const pkgPath =
|
|
28717
|
+
const pkgPath = path53.join(packageRoot, "package.json");
|
|
28154
28718
|
const pkg = JSON.parse(readFileSync34(pkgPath, "utf-8"));
|
|
28155
28719
|
return pkg.version;
|
|
28156
28720
|
}
|
|
@@ -28218,12 +28782,12 @@ function registerCliParityTools(server2) {
|
|
|
28218
28782
|
title: "Doctor",
|
|
28219
28783
|
description: "Run exe-os doctor audit. Defaults to read-only diagnostics; optional dry-run/fix flags mirror CLI.",
|
|
28220
28784
|
inputSchema: {
|
|
28221
|
-
agent:
|
|
28222
|
-
project:
|
|
28223
|
-
verbose:
|
|
28224
|
-
conflicts:
|
|
28225
|
-
dry_run:
|
|
28226
|
-
fix:
|
|
28785
|
+
agent: z91.string().optional(),
|
|
28786
|
+
project: z91.string().optional(),
|
|
28787
|
+
verbose: z91.boolean().default(false),
|
|
28788
|
+
conflicts: z91.boolean().default(false),
|
|
28789
|
+
dry_run: z91.boolean().default(false),
|
|
28790
|
+
fix: z91.boolean().default(false)
|
|
28227
28791
|
}
|
|
28228
28792
|
}, async ({ agent, project, verbose, conflicts, dry_run, fix }) => {
|
|
28229
28793
|
const args = [];
|
|
@@ -28240,9 +28804,9 @@ function registerCliParityTools(server2) {
|
|
|
28240
28804
|
title: "Rename Employee",
|
|
28241
28805
|
description: "Rename an employee using the same path as `exe-os rename <old> <new>`. Use for customer roster/identity renames.",
|
|
28242
28806
|
inputSchema: {
|
|
28243
|
-
old_name:
|
|
28244
|
-
new_name:
|
|
28245
|
-
dry_run:
|
|
28807
|
+
old_name: z91.string().min(1),
|
|
28808
|
+
new_name: z91.string().min(1),
|
|
28809
|
+
dry_run: z91.boolean().default(false)
|
|
28246
28810
|
}
|
|
28247
28811
|
}, async ({ old_name, new_name, dry_run }) => {
|
|
28248
28812
|
if (dry_run) {
|
|
@@ -28255,7 +28819,7 @@ function registerCliParityTools(server2) {
|
|
|
28255
28819
|
server2.registerTool("status_brief", {
|
|
28256
28820
|
title: "Status Brief",
|
|
28257
28821
|
description: "Return current employee/tmux status. Mirrors `exe-status` and supports optional deep view for one employee.",
|
|
28258
|
-
inputSchema: { employee:
|
|
28822
|
+
inputSchema: { employee: z91.string().optional() }
|
|
28259
28823
|
}, async ({ employee }) => {
|
|
28260
28824
|
const text3 = await status(employee);
|
|
28261
28825
|
return result2(text3, { ok: true, employee: employee ?? null });
|
|
@@ -28263,7 +28827,7 @@ function registerCliParityTools(server2) {
|
|
|
28263
28827
|
server2.registerTool("pending_work_summary", {
|
|
28264
28828
|
title: "Pending Work Summary",
|
|
28265
28829
|
description: "Return pending reviews, messages, and notifications using the same summaries as the CLI tools.",
|
|
28266
|
-
inputSchema: { agent:
|
|
28830
|
+
inputSchema: { agent: z91.string().optional() }
|
|
28267
28831
|
}, async ({ agent }) => {
|
|
28268
28832
|
const parts = await Promise.all([
|
|
28269
28833
|
runCommand("exe-pending-reviews", [], 3e4),
|
|
@@ -28284,7 +28848,7 @@ function registerCliParityTools(server2) {
|
|
|
28284
28848
|
server2.registerTool("key_rotation_preflight", {
|
|
28285
28849
|
title: "Key Rotation Preflight",
|
|
28286
28850
|
description: "Dry-run key rotation/update preflight. No destructive changes.",
|
|
28287
|
-
inputSchema: { mode:
|
|
28851
|
+
inputSchema: { mode: z91.enum(["rotate", "update"]).default("rotate") }
|
|
28288
28852
|
}, async ({ mode }) => {
|
|
28289
28853
|
const out = await runCommand("exe-os", ["key", mode, "--dry-run"], 6e4);
|
|
28290
28854
|
return result2(out.text, { ok: out.ok, command: out.command, mode }, !out.ok);
|
|
@@ -28303,11 +28867,11 @@ function registerCliParityTools(server2) {
|
|
|
28303
28867
|
title: "Stack Update Check",
|
|
28304
28868
|
description: "Plan/check a customer stack update without applying Docker changes. Mirrors `exe-os stack-update --check`.",
|
|
28305
28869
|
inputSchema: {
|
|
28306
|
-
target:
|
|
28307
|
-
manifest:
|
|
28308
|
-
compose_file:
|
|
28309
|
-
env_file:
|
|
28310
|
-
deployment_persona:
|
|
28870
|
+
target: z91.string().optional(),
|
|
28871
|
+
manifest: z91.string().optional(),
|
|
28872
|
+
compose_file: z91.string().optional(),
|
|
28873
|
+
env_file: z91.string().optional(),
|
|
28874
|
+
deployment_persona: z91.enum(["customer", "askexe-control-plane"]).default("customer")
|
|
28311
28875
|
}
|
|
28312
28876
|
}, async ({ target, manifest, compose_file, env_file, deployment_persona }) => {
|
|
28313
28877
|
const args = ["stack-update", "--check", "--deployment-persona", deployment_persona];
|
|
@@ -28331,7 +28895,7 @@ function registerCliParityTools(server2) {
|
|
|
28331
28895
|
// src/mcp/tools/get-session-events.ts
|
|
28332
28896
|
init_active_agent();
|
|
28333
28897
|
init_fast_db_init();
|
|
28334
|
-
import { z as
|
|
28898
|
+
import { z as z92 } from "zod";
|
|
28335
28899
|
|
|
28336
28900
|
// src/lib/session-events.ts
|
|
28337
28901
|
init_task_scope();
|
|
@@ -28418,7 +28982,7 @@ async function listRecentSessionEvents(client, options) {
|
|
|
28418
28982
|
}
|
|
28419
28983
|
|
|
28420
28984
|
// src/mcp/tools/get-session-events.ts
|
|
28421
|
-
var EVENT_TYPE =
|
|
28985
|
+
var EVENT_TYPE = z92.enum([
|
|
28422
28986
|
"user_prompt",
|
|
28423
28987
|
"assistant_response",
|
|
28424
28988
|
"tool_call",
|
|
@@ -28448,11 +29012,11 @@ function registerGetSessionEvents(server2) {
|
|
|
28448
29012
|
title: "Get Session Events",
|
|
28449
29013
|
description: "Return exact recent chronological user prompts, assistant responses, and tool calls from the append-only session journal. Use this when asked what happened last, not semantic memory search.",
|
|
28450
29014
|
inputSchema: {
|
|
28451
|
-
agent_id:
|
|
28452
|
-
session_id:
|
|
29015
|
+
agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
29016
|
+
session_id: z92.string().optional().describe("Optional exact runtime session id."),
|
|
28453
29017
|
event_type: EVENT_TYPE.optional().describe("Filter to one event type."),
|
|
28454
|
-
project_name:
|
|
28455
|
-
limit:
|
|
29018
|
+
project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects."),
|
|
29019
|
+
limit: z92.number().int().min(1).max(100).default(20).describe("Number of events to return.")
|
|
28456
29020
|
}
|
|
28457
29021
|
},
|
|
28458
29022
|
async ({ agent_id, session_id, event_type, project_name, limit }) => {
|
|
@@ -28488,8 +29052,8 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
28488
29052
|
title: "Get Last Assistant Response",
|
|
28489
29053
|
description: "Return the exact last assistant response for an agent from the session event journal. Use for 'what was the last thing you said?'",
|
|
28490
29054
|
inputSchema: {
|
|
28491
|
-
agent_id:
|
|
28492
|
-
project_name:
|
|
29055
|
+
agent_id: z92.string().optional().describe("Agent to inspect. Defaults to active agent. COO/CTO may inspect others."),
|
|
29056
|
+
project_name: z92.string().optional().describe("Optional project filter. Pass 'all' for all projects.")
|
|
28493
29057
|
}
|
|
28494
29058
|
},
|
|
28495
29059
|
async ({ agent_id, project_name }) => {
|
|
@@ -28519,32 +29083,106 @@ function registerGetLastAssistantResponse(server2) {
|
|
|
28519
29083
|
}
|
|
28520
29084
|
|
|
28521
29085
|
// src/mcp/tools/code-context.ts
|
|
28522
|
-
import { z as
|
|
29086
|
+
import { z as z93 } from "zod";
|
|
28523
29087
|
|
|
28524
29088
|
// src/lib/code-context-index.ts
|
|
28525
29089
|
init_config();
|
|
28526
|
-
import
|
|
28527
|
-
import
|
|
29090
|
+
import crypto20 from "crypto";
|
|
29091
|
+
import path54 from "path";
|
|
28528
29092
|
import { existsSync as existsSync40, mkdirSync as mkdirSync22, readFileSync as readFileSync35, readdirSync as readdirSync15, statSync as statSync9, writeFileSync as writeFileSync24 } from "fs";
|
|
28529
29093
|
import { spawnSync } from "child_process";
|
|
29094
|
+
init_exe_daemon_client();
|
|
29095
|
+
var VECTOR_STORE_VERSION = 1;
|
|
29096
|
+
var EMBED_BATCH_SIZE = 64;
|
|
29097
|
+
function vectorStorePath(projectRoot) {
|
|
29098
|
+
const rootHash = hashText(projectRoot).slice(0, 16);
|
|
29099
|
+
return path54.join(indexDir(), `${rootHash}.vectors.json`);
|
|
29100
|
+
}
|
|
29101
|
+
function loadVectorStore(projectRoot) {
|
|
29102
|
+
const file = vectorStorePath(projectRoot);
|
|
29103
|
+
if (!existsSync40(file)) return null;
|
|
29104
|
+
try {
|
|
29105
|
+
const parsed = JSON.parse(readFileSync35(file, "utf8"));
|
|
29106
|
+
if (parsed.version !== VECTOR_STORE_VERSION) return null;
|
|
29107
|
+
return parsed;
|
|
29108
|
+
} catch {
|
|
29109
|
+
return null;
|
|
29110
|
+
}
|
|
29111
|
+
}
|
|
29112
|
+
function saveVectorStore(projectRoot, store) {
|
|
29113
|
+
writeFileSync24(vectorStorePath(projectRoot), JSON.stringify(store));
|
|
29114
|
+
}
|
|
29115
|
+
function cosineSimilarity3(a, b) {
|
|
29116
|
+
let dot = 0, normA = 0, normB = 0;
|
|
29117
|
+
for (let i = 0; i < a.length; i++) {
|
|
29118
|
+
dot += a[i] * b[i];
|
|
29119
|
+
normA += a[i] * a[i];
|
|
29120
|
+
normB += b[i] * b[i];
|
|
29121
|
+
}
|
|
29122
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
29123
|
+
return denom === 0 ? 0 : dot / denom;
|
|
29124
|
+
}
|
|
29125
|
+
async function embedSymbols(index) {
|
|
29126
|
+
const rootHash = hashText(index.projectRoot).slice(0, 16);
|
|
29127
|
+
const existing = loadVectorStore(index.projectRoot);
|
|
29128
|
+
const store = {
|
|
29129
|
+
version: VECTOR_STORE_VERSION,
|
|
29130
|
+
projectRootHash: rootHash,
|
|
29131
|
+
vectors: {}
|
|
29132
|
+
};
|
|
29133
|
+
const allSymbols = [];
|
|
29134
|
+
for (const file of Object.values(index.files)) {
|
|
29135
|
+
for (const symbol of file.symbols) {
|
|
29136
|
+
if (existing?.vectors[symbol.id]) {
|
|
29137
|
+
store.vectors[symbol.id] = existing.vectors[symbol.id];
|
|
29138
|
+
} else {
|
|
29139
|
+
allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
|
|
29140
|
+
}
|
|
29141
|
+
}
|
|
29142
|
+
}
|
|
29143
|
+
if (allSymbols.length === 0) {
|
|
29144
|
+
saveVectorStore(index.projectRoot, store);
|
|
29145
|
+
return store;
|
|
29146
|
+
}
|
|
29147
|
+
const connected = await connectEmbedDaemon().catch(() => false);
|
|
29148
|
+
if (!connected) {
|
|
29149
|
+
saveVectorStore(index.projectRoot, store);
|
|
29150
|
+
return store;
|
|
29151
|
+
}
|
|
29152
|
+
for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
|
|
29153
|
+
const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
|
|
29154
|
+
const texts = batch.map((s) => s.text);
|
|
29155
|
+
try {
|
|
29156
|
+
const vectors = await embedBatchViaClient(texts, "low");
|
|
29157
|
+
if (vectors && vectors.length === batch.length) {
|
|
29158
|
+
for (let j = 0; j < batch.length; j++) {
|
|
29159
|
+
store.vectors[batch[j].id] = vectors[j];
|
|
29160
|
+
}
|
|
29161
|
+
}
|
|
29162
|
+
} catch {
|
|
29163
|
+
}
|
|
29164
|
+
}
|
|
29165
|
+
saveVectorStore(index.projectRoot, store);
|
|
29166
|
+
return store;
|
|
29167
|
+
}
|
|
28530
29168
|
var INDEX_VERSION = 2;
|
|
28531
29169
|
var DEFAULT_MAX_FILES = 5e3;
|
|
28532
29170
|
var IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
|
|
28533
29171
|
function normalizeProjectRoot(projectRoot) {
|
|
28534
|
-
return
|
|
29172
|
+
return path54.resolve(projectRoot || process.cwd());
|
|
28535
29173
|
}
|
|
28536
29174
|
function hashText(text3) {
|
|
28537
|
-
return
|
|
29175
|
+
return crypto20.createHash("sha256").update(text3).digest("hex");
|
|
28538
29176
|
}
|
|
28539
29177
|
function indexDir() {
|
|
28540
|
-
const dir =
|
|
29178
|
+
const dir = path54.join(EXE_AI_DIR, "code-context");
|
|
28541
29179
|
mkdirSync22(dir, { recursive: true });
|
|
28542
29180
|
return dir;
|
|
28543
29181
|
}
|
|
28544
29182
|
function getCodeContextIndexPath(projectRoot) {
|
|
28545
29183
|
const root = normalizeProjectRoot(projectRoot);
|
|
28546
29184
|
const rootHash = hashText(root).slice(0, 16);
|
|
28547
|
-
return
|
|
29185
|
+
return path54.join(indexDir(), `${rootHash}.json`);
|
|
28548
29186
|
}
|
|
28549
29187
|
function currentBranch(projectRoot) {
|
|
28550
29188
|
const result3 = spawnSync("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
|
|
@@ -28557,8 +29195,8 @@ function shouldIgnore(relPath) {
|
|
|
28557
29195
|
}
|
|
28558
29196
|
function listRecursive(projectRoot, dir = projectRoot, out = []) {
|
|
28559
29197
|
for (const entry of readdirSync15(dir, { withFileTypes: true })) {
|
|
28560
|
-
const abs =
|
|
28561
|
-
const rel =
|
|
29198
|
+
const abs = path54.join(dir, entry.name);
|
|
29199
|
+
const rel = path54.relative(projectRoot, abs).replaceAll(path54.sep, "/");
|
|
28562
29200
|
if (shouldIgnore(rel)) continue;
|
|
28563
29201
|
if (entry.isDirectory()) listRecursive(projectRoot, abs, out);
|
|
28564
29202
|
else if (entry.isFile()) out.push(rel);
|
|
@@ -28574,7 +29212,7 @@ function listCodeFiles(projectRoot, maxFiles) {
|
|
|
28574
29212
|
const rg = spawnSync("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
|
|
28575
29213
|
files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
|
|
28576
29214
|
}
|
|
28577
|
-
return files.map((file) => file.replaceAll(
|
|
29215
|
+
return files.map((file) => file.replaceAll(path54.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
|
|
28578
29216
|
}
|
|
28579
29217
|
function parseImportPaths(importText) {
|
|
28580
29218
|
const paths = [];
|
|
@@ -28587,13 +29225,13 @@ function parseImportPaths(importText) {
|
|
|
28587
29225
|
}
|
|
28588
29226
|
function resolveImport(fromFile, importPath, allFiles) {
|
|
28589
29227
|
if (!importPath.startsWith(".")) return null;
|
|
28590
|
-
const base =
|
|
29228
|
+
const base = path54.posix.normalize(path54.posix.join(path54.posix.dirname(fromFile.replaceAll(path54.sep, "/")), importPath));
|
|
28591
29229
|
const withoutKnownExt = base.replace(/\.(?:[a-z0-9]+)$/i, "");
|
|
28592
29230
|
const candidates = [base];
|
|
28593
29231
|
for (const ext of ["ts", "tsx", "js", "jsx", "py", "rs", "go", "java", "cs", "cpp", "c", "rb", "php", "swift", "kt", "scala", "sql", "md", "json", "yaml", "yml"]) {
|
|
28594
29232
|
candidates.push(`${withoutKnownExt}.${ext}`, `${base}.${ext}`);
|
|
28595
29233
|
}
|
|
28596
|
-
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(
|
|
29234
|
+
for (const indexName of ["index.ts", "index.tsx", "index.js", "mod.rs", "__init__.py"]) candidates.push(path54.posix.join(base, indexName));
|
|
28597
29235
|
return candidates.find((candidate) => allFiles.has(candidate)) ?? null;
|
|
28598
29236
|
}
|
|
28599
29237
|
function symbolId(filePath, chunk) {
|
|
@@ -28614,7 +29252,7 @@ function saveIndex(index) {
|
|
|
28614
29252
|
writeFileSync24(getCodeContextIndexPath(index.projectRoot), JSON.stringify(index, null, 2));
|
|
28615
29253
|
}
|
|
28616
29254
|
function buildFileRecord(projectRoot, relPath, allFiles, previous) {
|
|
28617
|
-
const absPath =
|
|
29255
|
+
const absPath = path54.join(projectRoot, relPath);
|
|
28618
29256
|
let stat;
|
|
28619
29257
|
try {
|
|
28620
29258
|
stat = statSync9(absPath);
|
|
@@ -28652,13 +29290,13 @@ function buildCodeContextIndex(options = {}) {
|
|
|
28652
29290
|
const branch = currentBranch(projectRoot);
|
|
28653
29291
|
const previous = options.force ? null : loadIndex(projectRoot);
|
|
28654
29292
|
const files = listCodeFiles(projectRoot, maxFiles);
|
|
28655
|
-
const allFiles = new Set(files.map((file) => file.replaceAll(
|
|
29293
|
+
const allFiles = new Set(files.map((file) => file.replaceAll(path54.sep, "/")));
|
|
28656
29294
|
const fileRecords = {};
|
|
28657
29295
|
let rebuiltFiles = 0;
|
|
28658
29296
|
let reusedFiles = 0;
|
|
28659
29297
|
let skippedFiles = 0;
|
|
28660
29298
|
for (const rel of files) {
|
|
28661
|
-
const normalized = rel.replaceAll(
|
|
29299
|
+
const normalized = rel.replaceAll(path54.sep, "/");
|
|
28662
29300
|
const { record, reused } = buildFileRecord(projectRoot, normalized, allFiles, previous?.files[normalized]);
|
|
28663
29301
|
if (record) {
|
|
28664
29302
|
fileRecords[normalized] = record;
|
|
@@ -28687,11 +29325,11 @@ function loadOrBuildCodeContextIndex(options = {}) {
|
|
|
28687
29325
|
if (loaded) {
|
|
28688
29326
|
const currentFiles = listCodeFiles(projectRoot, options.maxFiles ?? DEFAULT_MAX_FILES);
|
|
28689
29327
|
const unchanged = currentFiles.every((rel) => {
|
|
28690
|
-
const normalized = rel.replaceAll(
|
|
29328
|
+
const normalized = rel.replaceAll(path54.sep, "/");
|
|
28691
29329
|
const existing = loaded.files[normalized];
|
|
28692
29330
|
if (!existing) return false;
|
|
28693
29331
|
try {
|
|
28694
|
-
const stat = statSync9(
|
|
29332
|
+
const stat = statSync9(path54.join(projectRoot, normalized));
|
|
28695
29333
|
return stat.mtimeMs === existing.mtimeMs && stat.size === existing.size;
|
|
28696
29334
|
} catch {
|
|
28697
29335
|
return false;
|
|
@@ -28744,9 +29382,9 @@ function globToRegex(pattern) {
|
|
|
28744
29382
|
}
|
|
28745
29383
|
function matchesPath(filePath, patterns) {
|
|
28746
29384
|
if (!patterns || patterns.length === 0) return true;
|
|
28747
|
-
const normalized = filePath.replaceAll(
|
|
29385
|
+
const normalized = filePath.replaceAll(path54.sep, "/");
|
|
28748
29386
|
return patterns.some((pattern) => {
|
|
28749
|
-
const p = pattern.replaceAll(
|
|
29387
|
+
const p = pattern.replaceAll(path54.sep, "/").replace(/^\.\//, "");
|
|
28750
29388
|
return normalized === p || normalized.startsWith(`${p}/`) || normalized.endsWith(p) || globToRegex(p).test(normalized);
|
|
28751
29389
|
});
|
|
28752
29390
|
}
|
|
@@ -28805,7 +29443,7 @@ function filteredFiles(index, options = {}) {
|
|
|
28805
29443
|
return matchesPath(file.path, options.paths);
|
|
28806
29444
|
});
|
|
28807
29445
|
}
|
|
28808
|
-
function
|
|
29446
|
+
function lexicalSearch(query, options = {}) {
|
|
28809
29447
|
const terms = tokenize(query);
|
|
28810
29448
|
if (terms.length === 0) return [];
|
|
28811
29449
|
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
@@ -28831,6 +29469,82 @@ function searchCodeContext(query, options = {}) {
|
|
|
28831
29469
|
const limit = options.limit ?? 20;
|
|
28832
29470
|
return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
28833
29471
|
}
|
|
29472
|
+
function searchCodeContext(query, options = {}) {
|
|
29473
|
+
return lexicalSearch(query, options);
|
|
29474
|
+
}
|
|
29475
|
+
var RRF_K2 = 60;
|
|
29476
|
+
async function searchCodeContextSemantic(query, options = {}) {
|
|
29477
|
+
const terms = tokenize(query);
|
|
29478
|
+
if (terms.length === 0) return [];
|
|
29479
|
+
const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
|
|
29480
|
+
const projectRoot = normalizeProjectRoot(options.projectRoot);
|
|
29481
|
+
const vectorStore = loadVectorStore(projectRoot);
|
|
29482
|
+
if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
|
|
29483
|
+
return lexicalSearch(query, options);
|
|
29484
|
+
}
|
|
29485
|
+
let queryVector = null;
|
|
29486
|
+
try {
|
|
29487
|
+
const connected = await connectEmbedDaemon().catch(() => false);
|
|
29488
|
+
if (connected) {
|
|
29489
|
+
const result3 = await embedBatchViaClient([query], "high");
|
|
29490
|
+
if (result3 && result3.length === 1) queryVector = result3[0];
|
|
29491
|
+
}
|
|
29492
|
+
} catch {
|
|
29493
|
+
}
|
|
29494
|
+
if (!queryVector) return lexicalSearch(query, options);
|
|
29495
|
+
const files = filteredFiles(index, options);
|
|
29496
|
+
const candidates = [];
|
|
29497
|
+
for (const file of files) {
|
|
29498
|
+
for (const symbol of file.symbols) {
|
|
29499
|
+
const lexical = scoreSymbol(symbol, terms);
|
|
29500
|
+
const vec = vectorStore.vectors[symbol.id];
|
|
29501
|
+
const vectorScore = vec ? cosineSimilarity3(queryVector, vec) : 0;
|
|
29502
|
+
if (lexical.score > 0 || vectorScore > 0.3) {
|
|
29503
|
+
candidates.push({
|
|
29504
|
+
symbol,
|
|
29505
|
+
lexicalScore: lexical.score,
|
|
29506
|
+
vectorScore,
|
|
29507
|
+
matches: lexical.matches
|
|
29508
|
+
});
|
|
29509
|
+
}
|
|
29510
|
+
}
|
|
29511
|
+
}
|
|
29512
|
+
const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
|
|
29513
|
+
const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
|
|
29514
|
+
const lexicalRank = /* @__PURE__ */ new Map();
|
|
29515
|
+
const vectorRank = /* @__PURE__ */ new Map();
|
|
29516
|
+
byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
|
|
29517
|
+
byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
|
|
29518
|
+
const VECTOR_WEIGHT = 0.6;
|
|
29519
|
+
const LEXICAL_WEIGHT = 0.4;
|
|
29520
|
+
const fused = candidates.map((c) => {
|
|
29521
|
+
const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
|
|
29522
|
+
const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
|
|
29523
|
+
const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K2 + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K2 + lRank));
|
|
29524
|
+
return {
|
|
29525
|
+
symbol: c.symbol,
|
|
29526
|
+
score: rrfScore,
|
|
29527
|
+
matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
|
|
29528
|
+
filePath: c.symbol.filePath,
|
|
29529
|
+
language: c.symbol.language,
|
|
29530
|
+
content: c.symbol.text,
|
|
29531
|
+
startLine: c.symbol.startLine,
|
|
29532
|
+
endLine: c.symbol.endLine
|
|
29533
|
+
};
|
|
29534
|
+
});
|
|
29535
|
+
const offset = Math.max(0, options.offset ?? 0);
|
|
29536
|
+
const limit = options.limit ?? 20;
|
|
29537
|
+
return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
|
|
29538
|
+
}
|
|
29539
|
+
async function buildCodeContextIndexWithEmbeddings(options = {}) {
|
|
29540
|
+
const index = buildCodeContextIndex(options);
|
|
29541
|
+
const existingStore = loadVectorStore(index.projectRoot);
|
|
29542
|
+
const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
|
|
29543
|
+
const store = await embedSymbols(index);
|
|
29544
|
+
const vectorCount = Object.keys(store.vectors).length;
|
|
29545
|
+
const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
|
|
29546
|
+
return { index, vectorCount, newEmbeddings };
|
|
29547
|
+
}
|
|
28834
29548
|
function dependentsMap(index) {
|
|
28835
29549
|
const map = /* @__PURE__ */ new Map();
|
|
28836
29550
|
for (const file of Object.values(index.files)) {
|
|
@@ -28864,7 +29578,7 @@ function traceCodeSymbol(symbolName, options = {}) {
|
|
|
28864
29578
|
}
|
|
28865
29579
|
function resolveTargetFile(index, input) {
|
|
28866
29580
|
if (input.filePath) {
|
|
28867
|
-
const normalized = input.filePath.replaceAll(
|
|
29581
|
+
const normalized = input.filePath.replaceAll(path54.sep, "/").replace(/^\.\//, "");
|
|
28868
29582
|
if (index.files[normalized]) return { filePath: normalized, target: normalized };
|
|
28869
29583
|
const suffix = Object.keys(index.files).find((file) => file.endsWith(normalized));
|
|
28870
29584
|
if (suffix) return { filePath: suffix, target: input.filePath };
|
|
@@ -28894,7 +29608,7 @@ function analyzeBlastRadius(input) {
|
|
|
28894
29608
|
}
|
|
28895
29609
|
}
|
|
28896
29610
|
}
|
|
28897
|
-
const targetBase =
|
|
29611
|
+
const targetBase = path54.basename(target.filePath).replace(/\.[^.]+$/, "").toLowerCase();
|
|
28898
29612
|
const symbolLower = input.symbol?.toLowerCase();
|
|
28899
29613
|
const tests = Object.keys(index.files).filter((file) => {
|
|
28900
29614
|
const lower = file.toLowerCase();
|
|
@@ -28933,23 +29647,24 @@ function jsonResult(value) {
|
|
|
28933
29647
|
function registerCodeContext(server2) {
|
|
28934
29648
|
server2.registerTool("code_context", {
|
|
28935
29649
|
title: "Code Context",
|
|
28936
|
-
description: "Persistent codebase context engine. One consolidated tool to avoid MCP bloat. Actions: index, search, trace, blast_radius, stats.",
|
|
29650
|
+
description: "Persistent codebase context engine with semantic vector search. One consolidated tool to avoid MCP bloat. Actions: index (structural only), index_embed (structural + vector embeddings), search (semantic+lexical hybrid), trace, blast_radius, stats. Search uses RRF fusion of Jina v5 embeddings (cosine similarity) + lexical scoring. Falls back to lexical if daemon unavailable.",
|
|
28937
29651
|
inputSchema: {
|
|
28938
|
-
action:
|
|
28939
|
-
project_root:
|
|
28940
|
-
query:
|
|
28941
|
-
symbol:
|
|
28942
|
-
file_path:
|
|
28943
|
-
force:
|
|
28944
|
-
limit:
|
|
28945
|
-
offset:
|
|
28946
|
-
refresh_index:
|
|
28947
|
-
languages:
|
|
28948
|
-
paths:
|
|
28949
|
-
depth:
|
|
28950
|
-
max_files:
|
|
28951
|
-
|
|
28952
|
-
|
|
29652
|
+
action: z93.enum(["index", "index_embed", "search", "trace", "blast_radius", "stats"]).describe("Code context operation. index_embed = index + generate embeddings for semantic search."),
|
|
29653
|
+
project_root: z93.string().optional().describe("Repository root. Defaults to current working directory."),
|
|
29654
|
+
query: z93.string().optional().describe("Natural language search query (e.g. 'authentication logic', 'database migration'). Semantic search finds conceptual matches, not just keywords."),
|
|
29655
|
+
symbol: z93.string().optional().describe("Symbol/function/class/type name for trace or blast_radius"),
|
|
29656
|
+
file_path: z93.string().optional().describe("File path for blast_radius"),
|
|
29657
|
+
force: z93.boolean().optional().describe("Force rebuild before answering"),
|
|
29658
|
+
limit: z93.coerce.number().int().min(1).max(100).optional().describe("Max results"),
|
|
29659
|
+
offset: z93.coerce.number().int().min(0).optional().describe("Search pagination offset"),
|
|
29660
|
+
refresh_index: z93.boolean().optional().describe("Refresh/rebuild index before searching"),
|
|
29661
|
+
languages: z93.array(z93.string()).optional().describe('Language filters, e.g. ["python", "typescript"]'),
|
|
29662
|
+
paths: z93.array(z93.string()).optional().describe("Path/glob filters"),
|
|
29663
|
+
depth: z93.coerce.number().int().min(1).max(5).optional().describe("Dependent traversal depth for blast_radius"),
|
|
29664
|
+
max_files: z93.coerce.number().int().min(1).max(1e4).optional().describe("Max code files to index"),
|
|
29665
|
+
lexical_only: z93.boolean().optional().describe("Force lexical-only search (skip vector similarity). Default: false \u2014 uses semantic hybrid search.")
|
|
29666
|
+
}
|
|
29667
|
+
}, async ({ action, project_root, query, symbol, file_path, force, limit, offset, refresh_index, languages, paths, depth, max_files, lexical_only }) => {
|
|
28953
29668
|
const opts = { projectRoot: project_root, force, maxFiles: max_files };
|
|
28954
29669
|
if (action === "index") {
|
|
28955
29670
|
const index = buildCodeContextIndex(opts);
|
|
@@ -28959,7 +29674,24 @@ function registerCodeContext(server2) {
|
|
|
28959
29674
|
indexedAt: index.indexedAt,
|
|
28960
29675
|
files: Object.keys(index.files).length,
|
|
28961
29676
|
symbols: Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0),
|
|
28962
|
-
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0)
|
|
29677
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
29678
|
+
note: "Structural index only. Use action=index_embed to also generate vector embeddings for semantic search."
|
|
29679
|
+
});
|
|
29680
|
+
}
|
|
29681
|
+
if (action === "index_embed") {
|
|
29682
|
+
const { index, vectorCount, newEmbeddings } = await buildCodeContextIndexWithEmbeddings(opts);
|
|
29683
|
+
const totalSymbols = Object.values(index.files).reduce((sum, file) => sum + file.symbols.length, 0);
|
|
29684
|
+
return jsonResult({
|
|
29685
|
+
projectRoot: index.projectRoot,
|
|
29686
|
+
branch: index.branch,
|
|
29687
|
+
indexedAt: index.indexedAt,
|
|
29688
|
+
files: Object.keys(index.files).length,
|
|
29689
|
+
symbols: totalSymbols,
|
|
29690
|
+
imports: Object.values(index.files).reduce((sum, file) => sum + file.resolvedImports.length, 0),
|
|
29691
|
+
vectorCount,
|
|
29692
|
+
newEmbeddings,
|
|
29693
|
+
embeddingCoverage: totalSymbols > 0 ? `${(vectorCount / totalSymbols * 100).toFixed(1)}%` : "0%",
|
|
29694
|
+
note: "Structural index + vector embeddings. Semantic search is now available."
|
|
28963
29695
|
});
|
|
28964
29696
|
}
|
|
28965
29697
|
if (action === "stats") {
|
|
@@ -28967,14 +29699,28 @@ function registerCodeContext(server2) {
|
|
|
28967
29699
|
}
|
|
28968
29700
|
if (action === "search") {
|
|
28969
29701
|
if (!query) return errorResult10('code_context action "search" requires query');
|
|
29702
|
+
const searchOpts = { ...opts, limit, offset, refreshIndex: refresh_index, languages, paths };
|
|
29703
|
+
if (lexical_only) {
|
|
29704
|
+
return jsonResult({
|
|
29705
|
+
query,
|
|
29706
|
+
mode: "lexical",
|
|
29707
|
+
limit: limit ?? 20,
|
|
29708
|
+
offset: offset ?? 0,
|
|
29709
|
+
languages: languages ?? [],
|
|
29710
|
+
paths: paths ?? [],
|
|
29711
|
+
results: searchCodeContext(query, searchOpts)
|
|
29712
|
+
});
|
|
29713
|
+
}
|
|
29714
|
+
const results = await searchCodeContextSemantic(query, searchOpts);
|
|
29715
|
+
const hasSemantic = results.some((r) => r.matches.some((m) => m.startsWith("semantic=")));
|
|
28970
29716
|
return jsonResult({
|
|
28971
29717
|
query,
|
|
29718
|
+
mode: hasSemantic ? "semantic+lexical" : "lexical-fallback",
|
|
28972
29719
|
limit: limit ?? 20,
|
|
28973
29720
|
offset: offset ?? 0,
|
|
28974
|
-
refresh_index: refresh_index ?? false,
|
|
28975
29721
|
languages: languages ?? [],
|
|
28976
29722
|
paths: paths ?? [],
|
|
28977
|
-
results
|
|
29723
|
+
results
|
|
28978
29724
|
});
|
|
28979
29725
|
}
|
|
28980
29726
|
if (action === "trace") {
|
|
@@ -28992,9 +29738,9 @@ function registerCodeContext(server2) {
|
|
|
28992
29738
|
}
|
|
28993
29739
|
|
|
28994
29740
|
// src/mcp/tools/support-inbox.ts
|
|
28995
|
-
import { z as
|
|
29741
|
+
import { z as z94 } from "zod";
|
|
28996
29742
|
var DEFAULT_ENDPOINT = "https://askexe.com/admin/support/bug-reports";
|
|
28997
|
-
var STATUS =
|
|
29743
|
+
var STATUS = z94.enum(["open", "triaged", "fixed", "closed", "wontfix"]);
|
|
28998
29744
|
function adminToken() {
|
|
28999
29745
|
return process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN;
|
|
29000
29746
|
}
|
|
@@ -29033,9 +29779,9 @@ function registerListBugReports(server2) {
|
|
|
29033
29779
|
title: "List Bug Reports",
|
|
29034
29780
|
description: "AskExe-internal only: list incoming customer bug reports from the support inbox.",
|
|
29035
29781
|
inputSchema: {
|
|
29036
|
-
status:
|
|
29037
|
-
severity:
|
|
29038
|
-
limit:
|
|
29782
|
+
status: z94.enum(["all", "open", "triaged", "fixed", "closed", "wontfix"]).default("open"),
|
|
29783
|
+
severity: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
29784
|
+
limit: z94.number().int().min(1).max(100).default(25)
|
|
29039
29785
|
}
|
|
29040
29786
|
},
|
|
29041
29787
|
async ({ status: status2, severity, limit }) => {
|
|
@@ -29054,7 +29800,7 @@ function registerGetBugReport(server2) {
|
|
|
29054
29800
|
{
|
|
29055
29801
|
title: "Get Bug Report",
|
|
29056
29802
|
description: "AskExe-internal only: fetch one customer bug report with full markdown payload.",
|
|
29057
|
-
inputSchema: { id:
|
|
29803
|
+
inputSchema: { id: z94.string().min(8) }
|
|
29058
29804
|
},
|
|
29059
29805
|
async ({ id }) => {
|
|
29060
29806
|
const data = await requestJson(`${endpoint()}/${encodeURIComponent(id)}`);
|
|
@@ -29069,12 +29815,12 @@ function registerTriageBugReport(server2) {
|
|
|
29069
29815
|
title: "Triage Bug Report",
|
|
29070
29816
|
description: "AskExe-internal only: update bug report status and link task/commit/release metadata.",
|
|
29071
29817
|
inputSchema: {
|
|
29072
|
-
id:
|
|
29818
|
+
id: z94.string().min(8),
|
|
29073
29819
|
status: STATUS.optional(),
|
|
29074
|
-
triage_notes:
|
|
29075
|
-
linked_task_id:
|
|
29076
|
-
linked_commit:
|
|
29077
|
-
fixed_version:
|
|
29820
|
+
triage_notes: z94.string().optional(),
|
|
29821
|
+
linked_task_id: z94.string().optional(),
|
|
29822
|
+
linked_commit: z94.string().optional(),
|
|
29823
|
+
fixed_version: z94.string().optional()
|
|
29078
29824
|
}
|
|
29079
29825
|
},
|
|
29080
29826
|
async ({ id, status: status2, triage_notes, linked_task_id, linked_commit, fixed_version }) => {
|
|
@@ -29086,6 +29832,294 @@ function registerTriageBugReport(server2) {
|
|
|
29086
29832
|
}
|
|
29087
29833
|
);
|
|
29088
29834
|
}
|
|
29835
|
+
var FEATURE_STATUS = z94.enum(["open", "planned", "in_progress", "shipped", "closed", "wontdo"]);
|
|
29836
|
+
function featureEndpoint() {
|
|
29837
|
+
return endpoint().replace(/\/bug-reports\/?$/, "/feature-requests");
|
|
29838
|
+
}
|
|
29839
|
+
function registerListFeatureRequests(server2) {
|
|
29840
|
+
server2.registerTool(
|
|
29841
|
+
"list_feature_requests",
|
|
29842
|
+
{
|
|
29843
|
+
title: "List Feature Requests",
|
|
29844
|
+
description: "AskExe-internal only: list incoming customer feature requests from the support inbox.",
|
|
29845
|
+
inputSchema: {
|
|
29846
|
+
status: z94.enum(["all", "open", "planned", "in_progress", "shipped", "closed", "wontdo"]).default("open"),
|
|
29847
|
+
priority: z94.enum(["p0", "p1", "p2", "p3"]).optional(),
|
|
29848
|
+
limit: z94.number().int().min(1).max(100).default(25)
|
|
29849
|
+
}
|
|
29850
|
+
},
|
|
29851
|
+
async ({ status: status2, priority, limit }) => {
|
|
29852
|
+
const url = new URL(featureEndpoint());
|
|
29853
|
+
url.searchParams.set("status", status2);
|
|
29854
|
+
url.searchParams.set("limit", String(limit));
|
|
29855
|
+
if (priority) url.searchParams.set("priority", priority);
|
|
29856
|
+
const data = await requestJson(url.toString());
|
|
29857
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29858
|
+
}
|
|
29859
|
+
);
|
|
29860
|
+
}
|
|
29861
|
+
function registerGetFeatureRequest(server2) {
|
|
29862
|
+
server2.registerTool(
|
|
29863
|
+
"get_feature_request",
|
|
29864
|
+
{
|
|
29865
|
+
title: "Get Feature Request",
|
|
29866
|
+
description: "AskExe-internal only: fetch one customer feature request with full payload.",
|
|
29867
|
+
inputSchema: { id: z94.string().min(8) }
|
|
29868
|
+
},
|
|
29869
|
+
async ({ id }) => {
|
|
29870
|
+
const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`);
|
|
29871
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29872
|
+
}
|
|
29873
|
+
);
|
|
29874
|
+
}
|
|
29875
|
+
function registerTriageFeatureRequest(server2) {
|
|
29876
|
+
server2.registerTool(
|
|
29877
|
+
"triage_feature_request",
|
|
29878
|
+
{
|
|
29879
|
+
title: "Triage Feature Request",
|
|
29880
|
+
description: "AskExe-internal only: update feature request status, response notes, and version metadata.",
|
|
29881
|
+
inputSchema: {
|
|
29882
|
+
id: z94.string().min(8),
|
|
29883
|
+
status: FEATURE_STATUS.optional(),
|
|
29884
|
+
response_notes: z94.string().optional(),
|
|
29885
|
+
target_version: z94.string().optional(),
|
|
29886
|
+
shipped_version: z94.string().optional()
|
|
29887
|
+
}
|
|
29888
|
+
},
|
|
29889
|
+
async ({ id, status: status2, response_notes, target_version, shipped_version }) => {
|
|
29890
|
+
const data = await requestJson(`${featureEndpoint()}/${encodeURIComponent(id)}`, {
|
|
29891
|
+
method: "PATCH",
|
|
29892
|
+
body: JSON.stringify({ status: status2, response_notes, target_version, shipped_version })
|
|
29893
|
+
});
|
|
29894
|
+
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
29895
|
+
}
|
|
29896
|
+
);
|
|
29897
|
+
}
|
|
29898
|
+
|
|
29899
|
+
// src/mcp/tools/decision.ts
|
|
29900
|
+
import { z as z95 } from "zod";
|
|
29901
|
+
function buildHandlers6() {
|
|
29902
|
+
const tools = /* @__PURE__ */ new Map();
|
|
29903
|
+
const fake = {
|
|
29904
|
+
registerTool(name, _config, handler) {
|
|
29905
|
+
tools.set(name, handler);
|
|
29906
|
+
}
|
|
29907
|
+
};
|
|
29908
|
+
registerStoreDecision(fake);
|
|
29909
|
+
registerGetDecision(fake);
|
|
29910
|
+
return tools;
|
|
29911
|
+
}
|
|
29912
|
+
var ACTION_TO_TOOL3 = {
|
|
29913
|
+
store: "store_decision",
|
|
29914
|
+
get: "get_decision"
|
|
29915
|
+
};
|
|
29916
|
+
function registerDecision(server2) {
|
|
29917
|
+
const handlers = buildHandlers6();
|
|
29918
|
+
server2.registerTool(
|
|
29919
|
+
"decision",
|
|
29920
|
+
{
|
|
29921
|
+
title: "Decision",
|
|
29922
|
+
description: "Store or retrieve authoritative decisions. Actions: store (persist a decision by domain), get (look up a decision by domain/query).",
|
|
29923
|
+
inputSchema: {
|
|
29924
|
+
action: z95.enum(["store", "get"]).describe("Decision operation"),
|
|
29925
|
+
domain: z95.string().optional().describe("Decision domain (e.g. 'architecture', 'hiring')"),
|
|
29926
|
+
decision: z95.string().optional().describe("Decision text for action=store"),
|
|
29927
|
+
rationale: z95.string().optional().describe("Why this decision was made (action=store)"),
|
|
29928
|
+
query: z95.string().optional().describe("Search query for action=get"),
|
|
29929
|
+
limit: z95.coerce.number().optional().describe("Max results for action=get")
|
|
29930
|
+
}
|
|
29931
|
+
},
|
|
29932
|
+
async (input, extra) => {
|
|
29933
|
+
const action = input.action;
|
|
29934
|
+
const toolName = ACTION_TO_TOOL3[action];
|
|
29935
|
+
if (!toolName) return { content: [{ type: "text", text: `Unknown decision action: ${action}` }], isError: true };
|
|
29936
|
+
const handler = handlers.get(toolName);
|
|
29937
|
+
if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
|
|
29938
|
+
const { action: _, ...args } = input;
|
|
29939
|
+
return handler(args, extra);
|
|
29940
|
+
}
|
|
29941
|
+
);
|
|
29942
|
+
}
|
|
29943
|
+
|
|
29944
|
+
// src/mcp/tools/session.ts
|
|
29945
|
+
import { z as z96 } from "zod";
|
|
29946
|
+
function buildHandlers7() {
|
|
29947
|
+
const tools = /* @__PURE__ */ new Map();
|
|
29948
|
+
const fake = {
|
|
29949
|
+
registerTool(name, _config, handler) {
|
|
29950
|
+
tools.set(name, handler);
|
|
29951
|
+
}
|
|
29952
|
+
};
|
|
29953
|
+
registerGetSessionEvents(fake);
|
|
29954
|
+
registerGetLastAssistantResponse(fake);
|
|
29955
|
+
return tools;
|
|
29956
|
+
}
|
|
29957
|
+
var ACTION_TO_TOOL4 = {
|
|
29958
|
+
events: "get_session_events",
|
|
29959
|
+
last_response: "get_last_assistant_response"
|
|
29960
|
+
};
|
|
29961
|
+
function registerSession2(server2) {
|
|
29962
|
+
const handlers = buildHandlers7();
|
|
29963
|
+
server2.registerTool(
|
|
29964
|
+
"session",
|
|
29965
|
+
{
|
|
29966
|
+
title: "Session",
|
|
29967
|
+
description: "Session inspection tools. Actions: events (get session event log), last_response (get the most recent assistant response text).",
|
|
29968
|
+
inputSchema: {
|
|
29969
|
+
action: z96.enum(["events", "last_response"]).describe("Session operation"),
|
|
29970
|
+
session_id: z96.string().optional().describe("Session ID for action=events"),
|
|
29971
|
+
limit: z96.coerce.number().optional().describe("Max events for action=events")
|
|
29972
|
+
}
|
|
29973
|
+
},
|
|
29974
|
+
async (input, extra) => {
|
|
29975
|
+
const action = input.action;
|
|
29976
|
+
const toolName = ACTION_TO_TOOL4[action];
|
|
29977
|
+
if (!toolName) return { content: [{ type: "text", text: `Unknown session action: ${action}` }], isError: true };
|
|
29978
|
+
const handler = handlers.get(toolName);
|
|
29979
|
+
if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
|
|
29980
|
+
const { action: _, ...args } = input;
|
|
29981
|
+
return handler(args, extra);
|
|
29982
|
+
}
|
|
29983
|
+
);
|
|
29984
|
+
}
|
|
29985
|
+
|
|
29986
|
+
// src/mcp/tools/support-consolidated.ts
|
|
29987
|
+
import { z as z97 } from "zod";
|
|
29988
|
+
function buildHandlers8() {
|
|
29989
|
+
const tools = /* @__PURE__ */ new Map();
|
|
29990
|
+
const fake = {
|
|
29991
|
+
registerTool(name, _config, handler) {
|
|
29992
|
+
tools.set(name, handler);
|
|
29993
|
+
}
|
|
29994
|
+
};
|
|
29995
|
+
registerCreateBugReport(fake);
|
|
29996
|
+
registerCreateFeatureRequest(fake);
|
|
29997
|
+
registerSupportTools(fake);
|
|
29998
|
+
registerListBugReports(fake);
|
|
29999
|
+
registerGetBugReport(fake);
|
|
30000
|
+
registerTriageBugReport(fake);
|
|
30001
|
+
registerListFeatureRequests(fake);
|
|
30002
|
+
registerGetFeatureRequest(fake);
|
|
30003
|
+
registerTriageFeatureRequest(fake);
|
|
30004
|
+
return tools;
|
|
30005
|
+
}
|
|
30006
|
+
var ACTION_TO_TOOL5 = {
|
|
30007
|
+
create_bug: "create_bug_report",
|
|
30008
|
+
create_feature: "create_feature_request",
|
|
30009
|
+
list_my_bugs: "list_my_bug_reports",
|
|
30010
|
+
list_my_features: "list_my_feature_requests",
|
|
30011
|
+
list_bugs: "list_bug_reports",
|
|
30012
|
+
get_bug: "get_bug_report",
|
|
30013
|
+
triage_bug: "triage_bug_report",
|
|
30014
|
+
list_features: "list_feature_requests",
|
|
30015
|
+
get_feature: "get_feature_request",
|
|
30016
|
+
triage_feature: "triage_feature_request",
|
|
30017
|
+
health: "support_health",
|
|
30018
|
+
test: "support_test"
|
|
30019
|
+
};
|
|
30020
|
+
function registerSupportConsolidated(server2) {
|
|
30021
|
+
const handlers = buildHandlers8();
|
|
30022
|
+
server2.registerTool(
|
|
30023
|
+
"support",
|
|
30024
|
+
{
|
|
30025
|
+
title: "Support",
|
|
30026
|
+
description: "Bug reports, feature requests, and support system. Actions: create_bug, create_feature, list_my_bugs, list_my_features, list_bugs, get_bug, triage_bug, list_features, get_feature, triage_feature, health, test.",
|
|
30027
|
+
inputSchema: {
|
|
30028
|
+
action: z97.enum([
|
|
30029
|
+
"create_bug",
|
|
30030
|
+
"create_feature",
|
|
30031
|
+
"list_my_bugs",
|
|
30032
|
+
"list_my_features",
|
|
30033
|
+
"list_bugs",
|
|
30034
|
+
"get_bug",
|
|
30035
|
+
"triage_bug",
|
|
30036
|
+
"list_features",
|
|
30037
|
+
"get_feature",
|
|
30038
|
+
"triage_feature",
|
|
30039
|
+
"health",
|
|
30040
|
+
"test"
|
|
30041
|
+
]).describe("Support operation"),
|
|
30042
|
+
title: z97.string().optional().describe("Bug/feature title"),
|
|
30043
|
+
description: z97.string().optional().describe("Bug/feature description"),
|
|
30044
|
+
steps_to_reproduce: z97.string().optional().describe("Steps to reproduce (bugs)"),
|
|
30045
|
+
expected_behavior: z97.string().optional().describe("Expected behavior (bugs)"),
|
|
30046
|
+
actual_behavior: z97.string().optional().describe("Actual behavior (bugs)"),
|
|
30047
|
+
severity: z97.string().optional().describe("Severity: p0/p1/p2/p3"),
|
|
30048
|
+
use_case: z97.string().optional().describe("Use case (features)"),
|
|
30049
|
+
proposed_solution: z97.string().optional().describe("Proposed solution (features)"),
|
|
30050
|
+
priority: z97.string().optional().describe("Priority (features)"),
|
|
30051
|
+
id: z97.string().optional().describe("Bug/feature ID for get/triage"),
|
|
30052
|
+
status: z97.string().optional().describe("Filter by status"),
|
|
30053
|
+
classification: z97.string().optional().describe("Triage classification"),
|
|
30054
|
+
resolution: z97.string().optional().describe("Triage resolution"),
|
|
30055
|
+
notes: z97.string().optional().describe("Triage notes (mapped to triage_notes for bug/feature triage)"),
|
|
30056
|
+
triage_notes: z97.string().optional().describe("Triage notes (legacy field name)"),
|
|
30057
|
+
linked_task_id: z97.string().optional().describe("Linked task ID for triage"),
|
|
30058
|
+
linked_commit: z97.string().optional().describe("Linked commit hash for triage"),
|
|
30059
|
+
fixed_version: z97.string().optional().describe("Version that fixes this bug"),
|
|
30060
|
+
limit: z97.coerce.number().optional().describe("Max results")
|
|
30061
|
+
}
|
|
30062
|
+
},
|
|
30063
|
+
async (input, extra) => {
|
|
30064
|
+
const action = input.action;
|
|
30065
|
+
const toolName = ACTION_TO_TOOL5[action];
|
|
30066
|
+
if (!toolName) return { content: [{ type: "text", text: `Unknown support action: ${action}` }], isError: true };
|
|
30067
|
+
const handler = handlers.get(toolName);
|
|
30068
|
+
if (!handler) return { content: [{ type: "text", text: `Handler not found: ${toolName}` }], isError: true };
|
|
30069
|
+
const { action: _, ...args } = input;
|
|
30070
|
+
if ((action === "triage_bug" || action === "triage_feature") && args.notes && !args.triage_notes) {
|
|
30071
|
+
args.triage_notes = args.notes;
|
|
30072
|
+
delete args.notes;
|
|
30073
|
+
}
|
|
30074
|
+
return handler(args, extra);
|
|
30075
|
+
}
|
|
30076
|
+
);
|
|
30077
|
+
}
|
|
30078
|
+
|
|
30079
|
+
// src/mcp/tools/diagnostics.ts
|
|
30080
|
+
import { z as z98 } from "zod";
|
|
30081
|
+
function buildHandlers9() {
|
|
30082
|
+
const tools = /* @__PURE__ */ new Map();
|
|
30083
|
+
const fake = {
|
|
30084
|
+
registerTool(name, _config, handler) {
|
|
30085
|
+
tools.set(name, handler);
|
|
30086
|
+
}
|
|
30087
|
+
};
|
|
30088
|
+
registerCliParityTools(fake);
|
|
30089
|
+
return tools;
|
|
30090
|
+
}
|
|
30091
|
+
function registerDiagnostics(server2) {
|
|
30092
|
+
const handlers = buildHandlers9();
|
|
30093
|
+
const toolNames = Array.from(handlers.keys());
|
|
30094
|
+
void (toolNames.length > 0 ? toolNames : ["healthcheck"]);
|
|
30095
|
+
server2.registerTool(
|
|
30096
|
+
"diagnostics",
|
|
30097
|
+
{
|
|
30098
|
+
title: "Diagnostics",
|
|
30099
|
+
description: `System diagnostics and admin operations. Actions: ${toolNames.join(", ")}. Covers health checks, update status, cloud status, key management, and employee rename.`,
|
|
30100
|
+
inputSchema: {
|
|
30101
|
+
action: z98.string().describe(`Diagnostic operation: ${toolNames.join(", ")}`),
|
|
30102
|
+
// Pass-through params used by various sub-tools
|
|
30103
|
+
name: z98.string().optional().describe("Employee name (rename_employee)"),
|
|
30104
|
+
new_name: z98.string().optional().describe("New employee name (rename_employee)"),
|
|
30105
|
+
query: z98.string().optional().describe("Search/filter query"),
|
|
30106
|
+
format: z98.string().optional().describe("Output format")
|
|
30107
|
+
}
|
|
30108
|
+
},
|
|
30109
|
+
async (input, extra) => {
|
|
30110
|
+
const action = input.action;
|
|
30111
|
+
const handler = handlers.get(action);
|
|
30112
|
+
if (!handler) {
|
|
30113
|
+
return {
|
|
30114
|
+
content: [{ type: "text", text: `Unknown diagnostics action: ${action}. Available: ${toolNames.join(", ")}` }],
|
|
30115
|
+
isError: true
|
|
30116
|
+
};
|
|
30117
|
+
}
|
|
30118
|
+
const { action: _, ...args } = input;
|
|
30119
|
+
return handler(args, extra);
|
|
30120
|
+
}
|
|
30121
|
+
);
|
|
30122
|
+
}
|
|
29089
30123
|
|
|
29090
30124
|
// src/mcp/tool-gates.ts
|
|
29091
30125
|
var TOOL_GATES = {
|
|
@@ -29142,6 +30176,11 @@ var TOOL_CATEGORIES = {
|
|
|
29142
30176
|
registerGetSessionEvents: "core",
|
|
29143
30177
|
registerGetLastAssistantResponse: "core",
|
|
29144
30178
|
registerCodeContext: "graph-read",
|
|
30179
|
+
// consolidated tools
|
|
30180
|
+
registerDecision: "core",
|
|
30181
|
+
registerSession: "core",
|
|
30182
|
+
registerSupportConsolidated: "core",
|
|
30183
|
+
registerDiagnostics: "admin",
|
|
29145
30184
|
registerListBugReports: "admin",
|
|
29146
30185
|
registerGetBugReport: "admin",
|
|
29147
30186
|
registerTriageBugReport: "admin",
|
|
@@ -29235,6 +30274,36 @@ var TOOL_CATEGORIES = {
|
|
|
29235
30274
|
registerListGlobalProcedures: "orchestration",
|
|
29236
30275
|
registerDeactivateGlobalProcedure: "orchestration"
|
|
29237
30276
|
};
|
|
30277
|
+
var PLAN_TIERS = {
|
|
30278
|
+
free: 0,
|
|
30279
|
+
pro: 1,
|
|
30280
|
+
team: 2,
|
|
30281
|
+
agency: 3,
|
|
30282
|
+
enterprise: 4
|
|
30283
|
+
};
|
|
30284
|
+
var PLAN_GATES = {
|
|
30285
|
+
core: "free",
|
|
30286
|
+
tasks: "free",
|
|
30287
|
+
comms: "free",
|
|
30288
|
+
reminders: "free",
|
|
30289
|
+
"graph-read": "free",
|
|
30290
|
+
licensing: "free",
|
|
30291
|
+
// always allow checking/managing own license
|
|
30292
|
+
"graph-write": "pro",
|
|
30293
|
+
wiki: "pro",
|
|
30294
|
+
crm: "pro",
|
|
30295
|
+
"raw-data": "pro",
|
|
30296
|
+
documents: "pro",
|
|
30297
|
+
gateway: "pro",
|
|
30298
|
+
admin: "pro",
|
|
30299
|
+
orchestration: "pro"
|
|
30300
|
+
};
|
|
30301
|
+
function isToolAllowedForPlan(registerFnName, plan) {
|
|
30302
|
+
const category = TOOL_CATEGORIES[registerFnName];
|
|
30303
|
+
if (!category) return true;
|
|
30304
|
+
const requiredPlan = PLAN_GATES[category] ?? "free";
|
|
30305
|
+
return PLAN_TIERS[plan] >= PLAN_TIERS[requiredPlan];
|
|
30306
|
+
}
|
|
29238
30307
|
var CHIEF_OF_STAFF_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
29239
30308
|
"registerRecallMyMemory",
|
|
29240
30309
|
"registerAskTeamMemory",
|
|
@@ -29270,9 +30339,17 @@ function isToolAllowed(registerFnName) {
|
|
|
29270
30339
|
}
|
|
29271
30340
|
|
|
29272
30341
|
// src/mcp/register-tools.ts
|
|
29273
|
-
|
|
30342
|
+
var ALWAYS_ALLOWED = /* @__PURE__ */ new Set([
|
|
30343
|
+
"registerMcpPing",
|
|
30344
|
+
"registerGetLicenseStatus",
|
|
30345
|
+
"registerActivateLicense"
|
|
30346
|
+
]);
|
|
30347
|
+
function registerAllTools(server2, license, hasKey) {
|
|
29274
30348
|
const gate = (name, fn) => {
|
|
29275
|
-
if (isToolAllowed(name))
|
|
30349
|
+
if (!isToolAllowed(name)) return;
|
|
30350
|
+
if (hasKey === false && !ALWAYS_ALLOWED.has(name)) return;
|
|
30351
|
+
if (license && !isToolAllowedForPlan(name, license.plan)) return;
|
|
30352
|
+
fn(server2);
|
|
29276
30353
|
};
|
|
29277
30354
|
const mcpToolSurface = process.env.EXE_MCP_TOOL_SURFACE ?? "legacy";
|
|
29278
30355
|
const exposeConsolidatedTasks = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
@@ -29293,6 +30370,14 @@ function registerAllTools(server2) {
|
|
|
29293
30370
|
const exposeLegacyGraph = mcpToolSurface !== "consolidated";
|
|
29294
30371
|
const exposeConsolidatedConfig = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
29295
30372
|
const exposeLegacyConfig = mcpToolSurface !== "consolidated";
|
|
30373
|
+
const exposeConsolidatedDecisions = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
30374
|
+
const exposeLegacyDecisions = mcpToolSurface !== "consolidated";
|
|
30375
|
+
const exposeConsolidatedSession = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
30376
|
+
const exposeLegacySession = mcpToolSurface !== "consolidated";
|
|
30377
|
+
const exposeConsolidatedSupport = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
30378
|
+
const exposeLegacySupport = mcpToolSurface !== "consolidated";
|
|
30379
|
+
const exposeConsolidatedDiagnostics = mcpToolSurface === "both" || mcpToolSurface === "consolidated";
|
|
30380
|
+
const exposeLegacyDiagnostics = mcpToolSurface !== "consolidated";
|
|
29296
30381
|
if (exposeConsolidatedMemory) {
|
|
29297
30382
|
gate("registerMemory", registerMemory);
|
|
29298
30383
|
}
|
|
@@ -29406,19 +30491,43 @@ function registerAllTools(server2) {
|
|
|
29406
30491
|
if (exposeLegacyMemory) {
|
|
29407
30492
|
gate("registerSearchEverything", registerSearchEverything);
|
|
29408
30493
|
}
|
|
29409
|
-
|
|
29410
|
-
|
|
29411
|
-
|
|
29412
|
-
|
|
29413
|
-
|
|
29414
|
-
|
|
29415
|
-
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
gate("registerListBugReports", registerListBugReports);
|
|
29419
|
-
gate("registerGetBugReport", registerGetBugReport);
|
|
29420
|
-
gate("registerTriageBugReport", registerTriageBugReport);
|
|
30494
|
+
if (exposeConsolidatedDecisions) {
|
|
30495
|
+
gate("registerDecision", registerDecision);
|
|
30496
|
+
}
|
|
30497
|
+
if (exposeLegacyDecisions) {
|
|
30498
|
+
gate("registerStoreDecision", registerStoreDecision);
|
|
30499
|
+
gate("registerGetDecision", registerGetDecision);
|
|
30500
|
+
}
|
|
30501
|
+
if (exposeConsolidatedSupport) {
|
|
30502
|
+
gate("registerSupportConsolidated", registerSupportConsolidated);
|
|
29421
30503
|
}
|
|
30504
|
+
if (exposeLegacySupport) {
|
|
30505
|
+
gate("registerCreateBugReport", registerCreateBugReport);
|
|
30506
|
+
gate("registerCreateFeatureRequest", registerCreateFeatureRequest);
|
|
30507
|
+
gate("registerSupportTools", registerSupportTools);
|
|
30508
|
+
if (process.env.ASKEXE_SUPPORT_ADMIN_TOKEN || process.env.EXE_SUPPORT_ADMIN_TOKEN) {
|
|
30509
|
+
gate("registerListBugReports", registerListBugReports);
|
|
30510
|
+
gate("registerGetBugReport", registerGetBugReport);
|
|
30511
|
+
gate("registerTriageBugReport", registerTriageBugReport);
|
|
30512
|
+
gate("registerListFeatureRequests", registerListFeatureRequests);
|
|
30513
|
+
gate("registerGetFeatureRequest", registerGetFeatureRequest);
|
|
30514
|
+
gate("registerTriageFeatureRequest", registerTriageFeatureRequest);
|
|
30515
|
+
}
|
|
30516
|
+
}
|
|
30517
|
+
if (exposeConsolidatedDiagnostics) {
|
|
30518
|
+
gate("registerDiagnostics", registerDiagnostics);
|
|
30519
|
+
}
|
|
30520
|
+
if (exposeLegacyDiagnostics) {
|
|
30521
|
+
gate("registerCliParityTools", registerCliParityTools);
|
|
30522
|
+
}
|
|
30523
|
+
if (exposeConsolidatedSession) {
|
|
30524
|
+
gate("registerSession", registerSession2);
|
|
30525
|
+
}
|
|
30526
|
+
if (exposeLegacySession) {
|
|
30527
|
+
gate("registerGetSessionEvents", registerGetSessionEvents);
|
|
30528
|
+
gate("registerGetLastAssistantResponse", registerGetLastAssistantResponse);
|
|
30529
|
+
}
|
|
30530
|
+
gate("registerCodeContext", registerCodeContext);
|
|
29422
30531
|
if (exposeLegacyConfig) {
|
|
29423
30532
|
gate("registerGetAgentSpend", registerGetAgentSpend);
|
|
29424
30533
|
}
|
|
@@ -29533,9 +30642,13 @@ var _backfillTimer = null;
|
|
|
29533
30642
|
var _ppidWatchdog = null;
|
|
29534
30643
|
var _shuttingDown = false;
|
|
29535
30644
|
instrumentServer(server);
|
|
29536
|
-
|
|
30645
|
+
var wrappedServer = wrapServerWithTelemetry(server);
|
|
30646
|
+
var _licenseGate = await initLicenseGate();
|
|
30647
|
+
registerAllTools(wrappedServer, _licenseGate.license, _licenseGate.hasKey);
|
|
30648
|
+
startToolTelemetryFlush();
|
|
29537
30649
|
var _gatedRole = process.env.AGENT_ROLE ?? "standalone";
|
|
29538
|
-
|
|
30650
|
+
var _gatedPlan = _licenseGate.license?.plan ?? (_licenseGate.hasKey ? "validating" : "no-key");
|
|
30651
|
+
process.stderr.write(`[exe-os] Tool gating: role=${_gatedRole} plan=${_gatedPlan}
|
|
29539
30652
|
`);
|
|
29540
30653
|
try {
|
|
29541
30654
|
await initStore();
|
|
@@ -29585,7 +30698,7 @@ try {
|
|
|
29585
30698
|
}
|
|
29586
30699
|
}, 3e4);
|
|
29587
30700
|
_ppidWatchdog.unref();
|
|
29588
|
-
const MCP_VERSION_PATH =
|
|
30701
|
+
const MCP_VERSION_PATH = path55.join(os20.homedir(), ".exe-os", "mcp-version");
|
|
29589
30702
|
let _currentMcpVersion = null;
|
|
29590
30703
|
try {
|
|
29591
30704
|
_currentMcpVersion = existsSync41(MCP_VERSION_PATH) ? readFileSync36(MCP_VERSION_PATH, "utf8").trim() : null;
|
|
@@ -29627,14 +30740,14 @@ try {
|
|
|
29627
30740
|
`
|
|
29628
30741
|
);
|
|
29629
30742
|
const thisFile = fileURLToPath7(import.meta.url);
|
|
29630
|
-
const backfillPath =
|
|
29631
|
-
|
|
30743
|
+
const backfillPath = path55.resolve(
|
|
30744
|
+
path55.dirname(thisFile),
|
|
29632
30745
|
"../bin/backfill-vectors.js"
|
|
29633
30746
|
);
|
|
29634
30747
|
if (existsSync41(backfillPath)) {
|
|
29635
30748
|
const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
29636
|
-
const logPath =
|
|
29637
|
-
mkdirSync23(
|
|
30749
|
+
const logPath = path55.join(exeDir, "workers.log");
|
|
30750
|
+
mkdirSync23(path55.dirname(logPath), { recursive: true });
|
|
29638
30751
|
let logFd = "ignore";
|
|
29639
30752
|
try {
|
|
29640
30753
|
logFd = openSync4(logPath, "a");
|