@askexenow/exe-os 0.9.110 → 0.9.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/agentic-ontology-backfill.js +8 -1
- package/dist/bin/agentic-reflection-backfill.js +8 -1
- package/dist/bin/agentic-semantic-label.js +8 -1
- package/dist/bin/backfill-conversations.js +8 -1
- package/dist/bin/backfill-responses.js +8 -1
- package/dist/bin/backfill-vectors.js +8 -1
- package/dist/bin/bulk-sync-postgres.js +8 -1
- package/dist/bin/cleanup-stale-review-tasks.js +8 -1
- package/dist/bin/cli.js +19 -4
- package/dist/bin/exe-agent.js +1 -1
- package/dist/bin/exe-assign.js +8 -1
- package/dist/bin/exe-boot.js +19 -4
- package/dist/bin/exe-call.js +1 -1
- package/dist/bin/exe-cloud.js +8 -1
- package/dist/bin/exe-dispatch.js +460 -5
- package/dist/bin/exe-doctor.js +8 -1
- package/dist/bin/exe-export-behaviors.js +13 -3
- package/dist/bin/exe-forget.js +8 -1
- package/dist/bin/exe-gateway.js +19 -4
- package/dist/bin/exe-heartbeat.js +8 -1
- package/dist/bin/exe-kill.js +8 -1
- package/dist/bin/exe-launch-agent.js +13 -3
- package/dist/bin/exe-new-employee.js +1 -1
- package/dist/bin/exe-pending-messages.js +8 -1
- package/dist/bin/exe-pending-notifications.js +8 -1
- package/dist/bin/exe-pending-reviews.js +8 -1
- package/dist/bin/exe-rename.js +8 -1
- package/dist/bin/exe-review.js +8 -1
- package/dist/bin/exe-search.js +8 -1
- package/dist/bin/exe-session-cleanup.js +460 -5
- package/dist/bin/exe-start-codex.js +13 -3
- package/dist/bin/exe-start-opencode.js +13 -3
- package/dist/bin/exe-status.js +8 -1
- package/dist/bin/exe-team.js +8 -1
- package/dist/bin/git-sweep.js +460 -5
- package/dist/bin/graph-backfill.js +8 -1
- package/dist/bin/graph-export.js +8 -1
- package/dist/bin/intercom-check.js +460 -5
- package/dist/bin/pre-publish.js +1 -1
- package/dist/bin/scan-tasks.js +460 -5
- package/dist/bin/setup.js +8 -1
- package/dist/bin/shard-migrate.js +8 -1
- package/dist/gateway/index.js +460 -5
- package/dist/hooks/bug-report-worker.js +460 -5
- package/dist/hooks/codex-stop-task-finalizer.js +467 -5
- package/dist/hooks/commit-complete.js +460 -5
- package/dist/hooks/error-recall.js +8 -1
- package/dist/hooks/ingest.js +8 -1
- package/dist/hooks/instructions-loaded.js +8 -1
- package/dist/hooks/notification.js +8 -1
- package/dist/hooks/post-compact.js +8 -1
- package/dist/hooks/post-tool-combined.js +8 -1
- package/dist/hooks/pre-compact.js +460 -5
- package/dist/hooks/pre-tool-use.js +8 -1
- package/dist/hooks/prompt-submit.js +469 -8
- package/dist/hooks/session-end.js +460 -5
- package/dist/hooks/session-start.js +8 -1
- package/dist/hooks/stop.js +8 -1
- package/dist/hooks/subagent-stop.js +8 -1
- package/dist/hooks/summary-worker.js +8 -1
- package/dist/index.js +469 -8
- package/dist/lib/cloud-sync.js +7 -0
- package/dist/lib/database.js +7 -0
- package/dist/lib/db.js +7 -0
- package/dist/lib/device-registry.js +7 -0
- package/dist/lib/employee-templates.js +1 -1
- package/dist/lib/exe-daemon.js +216 -12
- package/dist/lib/hybrid-search.js +8 -1
- package/dist/lib/schedules.js +8 -1
- package/dist/lib/skill-learning.js +488 -7
- package/dist/lib/store.js +8 -1
- package/dist/lib/tasks.js +452 -4
- package/dist/lib/tmux-routing.js +452 -4
- package/dist/mcp/server.js +69 -11
- package/dist/mcp/tools/create-task.js +452 -4
- package/dist/mcp/tools/update-task.js +452 -4
- package/dist/runtime/index.js +469 -8
- package/dist/tui/App.js +19 -4
- package/package.json +1 -1
package/dist/bin/exe-dispatch.js
CHANGED
|
@@ -2615,6 +2615,13 @@ async function ensureSchema() {
|
|
|
2615
2615
|
} catch (e) {
|
|
2616
2616
|
logCatchDebug("migration", e);
|
|
2617
2617
|
}
|
|
2618
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2619
|
+
try {
|
|
2620
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2621
|
+
} catch (e) {
|
|
2622
|
+
logCatchDebug("migration", e);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2618
2625
|
try {
|
|
2619
2626
|
await client.execute({
|
|
2620
2627
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -3882,6 +3889,23 @@ var init_database = __esm({
|
|
|
3882
3889
|
});
|
|
3883
3890
|
|
|
3884
3891
|
// src/lib/license.ts
|
|
3892
|
+
var license_exports = {};
|
|
3893
|
+
__export(license_exports, {
|
|
3894
|
+
LICENSE_PUBLIC_KEY_PEM: () => LICENSE_PUBLIC_KEY_PEM,
|
|
3895
|
+
PLAN_LIMITS: () => PLAN_LIMITS,
|
|
3896
|
+
assertVpsLicense: () => assertVpsLicense,
|
|
3897
|
+
checkLicense: () => checkLicense,
|
|
3898
|
+
getCachedLicense: () => getCachedLicense,
|
|
3899
|
+
isFeatureAllowed: () => isFeatureAllowed,
|
|
3900
|
+
loadDeviceId: () => loadDeviceId,
|
|
3901
|
+
loadLicense: () => loadLicense,
|
|
3902
|
+
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
3903
|
+
readCachedLicenseToken: () => readCachedLicenseToken,
|
|
3904
|
+
saveLicense: () => saveLicense,
|
|
3905
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
3906
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
3907
|
+
validateLicense: () => validateLicense
|
|
3908
|
+
});
|
|
3885
3909
|
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
|
|
3886
3910
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3887
3911
|
import { createRequire as createRequire2 } from "module";
|
|
@@ -3889,7 +3913,411 @@ import { pathToFileURL as pathToFileURL2 } from "url";
|
|
|
3889
3913
|
import os7 from "os";
|
|
3890
3914
|
import path9 from "path";
|
|
3891
3915
|
import { jwtVerify, importSPKI } from "jose";
|
|
3892
|
-
|
|
3916
|
+
async function fetchRetry(url, init) {
|
|
3917
|
+
try {
|
|
3918
|
+
return await fetch(url, init);
|
|
3919
|
+
} catch {
|
|
3920
|
+
await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
|
|
3921
|
+
return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
function loadDeviceId() {
|
|
3925
|
+
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
3926
|
+
try {
|
|
3927
|
+
if (existsSync10(deviceJsonPath)) {
|
|
3928
|
+
const data = JSON.parse(readFileSync8(deviceJsonPath, "utf8"));
|
|
3929
|
+
if (data.deviceId) return data.deviceId;
|
|
3930
|
+
}
|
|
3931
|
+
} catch {
|
|
3932
|
+
}
|
|
3933
|
+
try {
|
|
3934
|
+
if (existsSync10(DEVICE_ID_PATH)) {
|
|
3935
|
+
const id2 = readFileSync8(DEVICE_ID_PATH, "utf8").trim();
|
|
3936
|
+
if (id2) return id2;
|
|
3937
|
+
}
|
|
3938
|
+
} catch {
|
|
3939
|
+
}
|
|
3940
|
+
const id = randomUUID2();
|
|
3941
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3942
|
+
writeFileSync6(DEVICE_ID_PATH, id, "utf8");
|
|
3943
|
+
return id;
|
|
3944
|
+
}
|
|
3945
|
+
function loadLicense() {
|
|
3946
|
+
try {
|
|
3947
|
+
if (!existsSync10(LICENSE_PATH)) return null;
|
|
3948
|
+
return readFileSync8(LICENSE_PATH, "utf8").trim();
|
|
3949
|
+
} catch {
|
|
3950
|
+
return null;
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
function saveLicense(apiKey) {
|
|
3954
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3955
|
+
writeFileSync6(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
3956
|
+
}
|
|
3957
|
+
async function verifyLicenseJwt(token) {
|
|
3958
|
+
try {
|
|
3959
|
+
const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
|
|
3960
|
+
const { payload } = await jwtVerify(token, key, {
|
|
3961
|
+
algorithms: [LICENSE_JWT_ALG]
|
|
3962
|
+
});
|
|
3963
|
+
const plan = payload.plan ?? "free";
|
|
3964
|
+
const email = payload.sub ?? "";
|
|
3965
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
3966
|
+
return {
|
|
3967
|
+
valid: true,
|
|
3968
|
+
plan,
|
|
3969
|
+
email,
|
|
3970
|
+
expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
|
|
3971
|
+
deviceLimit: limits.devices,
|
|
3972
|
+
employeeLimit: limits.employees,
|
|
3973
|
+
memoryLimit: limits.memories
|
|
3974
|
+
};
|
|
3975
|
+
} catch {
|
|
3976
|
+
return null;
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
async function getCachedLicense() {
|
|
3980
|
+
try {
|
|
3981
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3982
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
3983
|
+
if (!raw.token || typeof raw.token !== "string") return null;
|
|
3984
|
+
return await verifyLicenseJwt(raw.token);
|
|
3985
|
+
} catch {
|
|
3986
|
+
return null;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
function readCachedLicenseToken() {
|
|
3990
|
+
try {
|
|
3991
|
+
if (!existsSync10(CACHE_PATH)) return null;
|
|
3992
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
3993
|
+
return typeof raw.token === "string" ? raw.token : null;
|
|
3994
|
+
} catch {
|
|
3995
|
+
return null;
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
function getRawCachedPlan() {
|
|
3999
|
+
try {
|
|
4000
|
+
const token = readCachedLicenseToken();
|
|
4001
|
+
if (!token) return null;
|
|
4002
|
+
const parts = token.split(".");
|
|
4003
|
+
if (parts.length !== 3) return null;
|
|
4004
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
|
|
4005
|
+
const plan = payload.plan ?? "free";
|
|
4006
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
4007
|
+
process.stderr.write(
|
|
4008
|
+
`[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
|
|
4009
|
+
`
|
|
4010
|
+
);
|
|
4011
|
+
return {
|
|
4012
|
+
valid: true,
|
|
4013
|
+
plan,
|
|
4014
|
+
email: payload.sub ?? "",
|
|
4015
|
+
expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
|
|
4016
|
+
deviceLimit: limits.devices,
|
|
4017
|
+
employeeLimit: limits.employees,
|
|
4018
|
+
memoryLimit: limits.memories
|
|
4019
|
+
};
|
|
4020
|
+
} catch {
|
|
4021
|
+
return null;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
function cacheResponse(token) {
|
|
4025
|
+
try {
|
|
4026
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
4027
|
+
} catch {
|
|
4028
|
+
}
|
|
4029
|
+
}
|
|
4030
|
+
function loadPrismaForLicense() {
|
|
4031
|
+
if (_prismaFailed) return null;
|
|
4032
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
4033
|
+
if (!dbUrl) {
|
|
4034
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os7.homedir(), "exe-db");
|
|
4035
|
+
if (!existsSync10(path9.join(exeDbRoot, "package.json"))) {
|
|
4036
|
+
_prismaFailed = true;
|
|
4037
|
+
return null;
|
|
4038
|
+
}
|
|
4039
|
+
}
|
|
4040
|
+
if (!_prismaPromise) {
|
|
4041
|
+
_prismaPromise = (async () => {
|
|
4042
|
+
const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
|
|
4043
|
+
if (explicitPath) {
|
|
4044
|
+
const mod2 = await import(pathToFileURL2(explicitPath).href);
|
|
4045
|
+
const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
|
|
4046
|
+
if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
|
|
4047
|
+
return new Ctor2();
|
|
4048
|
+
}
|
|
4049
|
+
const exeDbRoot = process.env.EXE_DB_ROOT ?? path9.join(os7.homedir(), "exe-db");
|
|
4050
|
+
const req = createRequire2(path9.join(exeDbRoot, "package.json"));
|
|
4051
|
+
const entry = req.resolve("@prisma/client");
|
|
4052
|
+
const mod = await import(pathToFileURL2(entry).href);
|
|
4053
|
+
const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
|
|
4054
|
+
if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
|
|
4055
|
+
return new Ctor();
|
|
4056
|
+
})().catch((err) => {
|
|
4057
|
+
_prismaFailed = true;
|
|
4058
|
+
_prismaPromise = null;
|
|
4059
|
+
throw err;
|
|
4060
|
+
});
|
|
4061
|
+
}
|
|
4062
|
+
return _prismaPromise;
|
|
4063
|
+
}
|
|
4064
|
+
async function validateViaPostgres(apiKey) {
|
|
4065
|
+
const loader = loadPrismaForLicense();
|
|
4066
|
+
if (!loader) return null;
|
|
4067
|
+
try {
|
|
4068
|
+
const prisma = await loader;
|
|
4069
|
+
const rows = await prisma.$queryRawUnsafe(
|
|
4070
|
+
`SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
|
|
4071
|
+
FROM billing.licenses WHERE key = $1 LIMIT 1`,
|
|
4072
|
+
apiKey
|
|
4073
|
+
);
|
|
4074
|
+
if (!rows || rows.length === 0) return null;
|
|
4075
|
+
const row = rows[0];
|
|
4076
|
+
if (row.status !== "active") return null;
|
|
4077
|
+
if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
|
|
4078
|
+
const plan = row.plan;
|
|
4079
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
4080
|
+
return {
|
|
4081
|
+
valid: true,
|
|
4082
|
+
plan,
|
|
4083
|
+
email: row.email,
|
|
4084
|
+
expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
|
|
4085
|
+
deviceLimit: row.device_limit ?? limits.devices,
|
|
4086
|
+
employeeLimit: row.employee_limit ?? limits.employees,
|
|
4087
|
+
memoryLimit: row.memory_limit ?? limits.memories
|
|
4088
|
+
};
|
|
4089
|
+
} catch {
|
|
4090
|
+
return null;
|
|
4091
|
+
}
|
|
4092
|
+
}
|
|
4093
|
+
async function validateViaCFWorker(apiKey, deviceId) {
|
|
4094
|
+
try {
|
|
4095
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
4096
|
+
method: "POST",
|
|
4097
|
+
headers: { "Content-Type": "application/json" },
|
|
4098
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
4099
|
+
signal: AbortSignal.timeout(1e4)
|
|
4100
|
+
});
|
|
4101
|
+
if (!res.ok) return null;
|
|
4102
|
+
const data = await res.json();
|
|
4103
|
+
if (data.error === "device_limit_exceeded") return null;
|
|
4104
|
+
if (!data.valid) return null;
|
|
4105
|
+
if (data.token) {
|
|
4106
|
+
cacheResponse(data.token);
|
|
4107
|
+
const verified = await verifyLicenseJwt(data.token);
|
|
4108
|
+
if (verified) return verified;
|
|
4109
|
+
}
|
|
4110
|
+
const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
|
|
4111
|
+
return {
|
|
4112
|
+
valid: data.valid,
|
|
4113
|
+
plan: data.plan,
|
|
4114
|
+
email: data.email,
|
|
4115
|
+
expiresAt: data.expiresAt,
|
|
4116
|
+
deviceLimit: limits.devices,
|
|
4117
|
+
employeeLimit: limits.employees,
|
|
4118
|
+
memoryLimit: limits.memories
|
|
4119
|
+
};
|
|
4120
|
+
} catch {
|
|
4121
|
+
return null;
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4124
|
+
async function validateLicense(apiKey, deviceId) {
|
|
4125
|
+
const did = deviceId ?? loadDeviceId();
|
|
4126
|
+
const pgResult = await validateViaPostgres(apiKey);
|
|
4127
|
+
if (pgResult) {
|
|
4128
|
+
try {
|
|
4129
|
+
writeFileSync6(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
|
|
4130
|
+
} catch {
|
|
4131
|
+
}
|
|
4132
|
+
return pgResult;
|
|
4133
|
+
}
|
|
4134
|
+
const cfResult = await validateViaCFWorker(apiKey, did);
|
|
4135
|
+
if (cfResult) return cfResult;
|
|
4136
|
+
const cached = await getCachedLicense();
|
|
4137
|
+
if (cached) return cached;
|
|
4138
|
+
try {
|
|
4139
|
+
if (existsSync10(CACHE_PATH)) {
|
|
4140
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH, "utf8"));
|
|
4141
|
+
if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
|
|
4142
|
+
return raw.pgLicense;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
} catch {
|
|
4146
|
+
}
|
|
4147
|
+
const rawFallback = getRawCachedPlan();
|
|
4148
|
+
if (rawFallback) return rawFallback;
|
|
4149
|
+
return { ...FREE_LICENSE, valid: false };
|
|
4150
|
+
}
|
|
4151
|
+
function getCacheAgeMs() {
|
|
4152
|
+
try {
|
|
4153
|
+
const { statSync: statSync5 } = __require("fs");
|
|
4154
|
+
const s = statSync5(CACHE_PATH);
|
|
4155
|
+
return Date.now() - s.mtimeMs;
|
|
4156
|
+
} catch {
|
|
4157
|
+
return Infinity;
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
async function checkLicense() {
|
|
4161
|
+
let key = loadLicense();
|
|
4162
|
+
if (!key) {
|
|
4163
|
+
try {
|
|
4164
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
4165
|
+
if (existsSync10(configPath)) {
|
|
4166
|
+
const raw = JSON.parse(readFileSync8(configPath, "utf8"));
|
|
4167
|
+
const cloud = raw.cloud;
|
|
4168
|
+
if (cloud?.apiKey) {
|
|
4169
|
+
key = cloud.apiKey;
|
|
4170
|
+
saveLicense(key);
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
} catch {
|
|
4174
|
+
}
|
|
4175
|
+
}
|
|
4176
|
+
if (!key) return FREE_LICENSE;
|
|
4177
|
+
const cached = await getCachedLicense();
|
|
4178
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
4179
|
+
const deviceId = loadDeviceId();
|
|
4180
|
+
return validateLicense(key, deviceId);
|
|
4181
|
+
}
|
|
4182
|
+
function isFeatureAllowed(license, feature) {
|
|
4183
|
+
switch (feature) {
|
|
4184
|
+
case "cloud_sync":
|
|
4185
|
+
case "external_agents":
|
|
4186
|
+
case "wiki":
|
|
4187
|
+
return license.plan !== "free";
|
|
4188
|
+
case "unlimited_employees":
|
|
4189
|
+
return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
function mirrorLicenseKey(apiKey) {
|
|
4193
|
+
const trimmed = apiKey.trim();
|
|
4194
|
+
if (!trimmed) return;
|
|
4195
|
+
saveLicense(trimmed);
|
|
4196
|
+
}
|
|
4197
|
+
async function assertVpsLicense(opts) {
|
|
4198
|
+
const env = opts?.env ?? process.env;
|
|
4199
|
+
const inProduction = env.NODE_ENV === "production";
|
|
4200
|
+
if (!opts?.force && !inProduction) {
|
|
4201
|
+
return { ...FREE_LICENSE, plan: "free" };
|
|
4202
|
+
}
|
|
4203
|
+
const envKey = env.EXE_LICENSE_KEY?.trim();
|
|
4204
|
+
if (envKey) {
|
|
4205
|
+
saveLicense(envKey);
|
|
4206
|
+
}
|
|
4207
|
+
const apiKey = envKey ?? loadLicense();
|
|
4208
|
+
if (!apiKey) {
|
|
4209
|
+
throw new Error(
|
|
4210
|
+
"License required: set EXE_LICENSE_KEY env var with your exe_sk_* key. Purchase at https://askexe.com. This VPS image refuses to boot without a valid license."
|
|
4211
|
+
);
|
|
4212
|
+
}
|
|
4213
|
+
const deviceId = loadDeviceId();
|
|
4214
|
+
let backendResponse = null;
|
|
4215
|
+
let explicitRejection = false;
|
|
4216
|
+
let transientFailure = false;
|
|
4217
|
+
try {
|
|
4218
|
+
const res = await fetchRetry(`${API_BASE}/auth/activate`, {
|
|
4219
|
+
method: "POST",
|
|
4220
|
+
headers: { "Content-Type": "application/json" },
|
|
4221
|
+
body: JSON.stringify({ apiKey, deviceId }),
|
|
4222
|
+
signal: AbortSignal.timeout(1e4)
|
|
4223
|
+
});
|
|
4224
|
+
if (res.ok) {
|
|
4225
|
+
backendResponse = await res.json();
|
|
4226
|
+
if (!backendResponse.valid) explicitRejection = true;
|
|
4227
|
+
} else if (res.status === 401 || res.status === 403) {
|
|
4228
|
+
explicitRejection = true;
|
|
4229
|
+
} else {
|
|
4230
|
+
transientFailure = true;
|
|
4231
|
+
}
|
|
4232
|
+
} catch {
|
|
4233
|
+
transientFailure = true;
|
|
4234
|
+
}
|
|
4235
|
+
if (backendResponse?.valid) {
|
|
4236
|
+
if (backendResponse.token) {
|
|
4237
|
+
cacheResponse(backendResponse.token);
|
|
4238
|
+
const verified = await verifyLicenseJwt(backendResponse.token);
|
|
4239
|
+
if (verified) return verified;
|
|
4240
|
+
}
|
|
4241
|
+
const limits = PLAN_LIMITS[backendResponse.plan] ?? PLAN_LIMITS.free;
|
|
4242
|
+
return {
|
|
4243
|
+
valid: true,
|
|
4244
|
+
plan: backendResponse.plan,
|
|
4245
|
+
email: backendResponse.email,
|
|
4246
|
+
expiresAt: backendResponse.expiresAt,
|
|
4247
|
+
deviceLimit: limits.devices,
|
|
4248
|
+
employeeLimit: limits.employees,
|
|
4249
|
+
memoryLimit: limits.memories
|
|
4250
|
+
};
|
|
4251
|
+
}
|
|
4252
|
+
if (explicitRejection) {
|
|
4253
|
+
throw new Error(
|
|
4254
|
+
`License invalid or expired. Renew at https://askexe.com, then restart. Backend rejected key ending in ...${apiKey.slice(-4)}.`
|
|
4255
|
+
);
|
|
4256
|
+
}
|
|
4257
|
+
if (!transientFailure) {
|
|
4258
|
+
throw new Error(
|
|
4259
|
+
"License validation failed: unknown backend state. Restore network connectivity to https://askexe.com/cloud and retry."
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
const fresh = await getCachedLicense();
|
|
4263
|
+
if (fresh && fresh.valid) return fresh;
|
|
4264
|
+
const graceDays = opts?.offlineGraceDays ?? 7;
|
|
4265
|
+
const graceMs = graceDays * 24 * 60 * 60 * 1e3;
|
|
4266
|
+
try {
|
|
4267
|
+
const token = readCachedLicenseToken();
|
|
4268
|
+
if (token) {
|
|
4269
|
+
const payloadB64 = token.split(".")[1];
|
|
4270
|
+
if (payloadB64) {
|
|
4271
|
+
const payload = JSON.parse(Buffer.from(payloadB64, "base64url").toString());
|
|
4272
|
+
const expMs = (payload.exp ?? 0) * 1e3;
|
|
4273
|
+
if (Date.now() < expMs + graceMs) {
|
|
4274
|
+
const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
|
|
4275
|
+
const { payload: verified } = await jwtVerify(token, key, {
|
|
4276
|
+
algorithms: [LICENSE_JWT_ALG],
|
|
4277
|
+
clockTolerance: graceDays * 24 * 60 * 60
|
|
4278
|
+
});
|
|
4279
|
+
const plan = verified.plan ?? "free";
|
|
4280
|
+
const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
|
|
4281
|
+
return {
|
|
4282
|
+
valid: true,
|
|
4283
|
+
plan,
|
|
4284
|
+
email: verified.sub ?? "",
|
|
4285
|
+
expiresAt: verified.exp ? new Date(verified.exp * 1e3).toISOString() : null,
|
|
4286
|
+
deviceLimit: limits.devices,
|
|
4287
|
+
employeeLimit: limits.employees,
|
|
4288
|
+
memoryLimit: limits.memories
|
|
4289
|
+
};
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
} catch {
|
|
4294
|
+
}
|
|
4295
|
+
throw new Error(
|
|
4296
|
+
`License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
|
|
4297
|
+
);
|
|
4298
|
+
}
|
|
4299
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
4300
|
+
if (_revalTimer) return;
|
|
4301
|
+
_revalTimer = setInterval(async () => {
|
|
4302
|
+
try {
|
|
4303
|
+
const license = await checkLicense();
|
|
4304
|
+
if (!license.valid) {
|
|
4305
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
4306
|
+
}
|
|
4307
|
+
} catch {
|
|
4308
|
+
}
|
|
4309
|
+
}, intervalMs);
|
|
4310
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
4311
|
+
_revalTimer.unref();
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
function stopLicenseRevalidation() {
|
|
4315
|
+
if (_revalTimer) {
|
|
4316
|
+
clearInterval(_revalTimer);
|
|
4317
|
+
_revalTimer = null;
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
|
|
3893
4321
|
var init_license = __esm({
|
|
3894
4322
|
"src/lib/license.ts"() {
|
|
3895
4323
|
"use strict";
|
|
@@ -3898,6 +4326,12 @@ var init_license = __esm({
|
|
|
3898
4326
|
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3899
4327
|
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
3900
4328
|
API_BASE = process.env.EXE_CLOUD_ENDPOINT ?? "https://askexe.com/cloud";
|
|
4329
|
+
RETRY_DELAY_MS = 500;
|
|
4330
|
+
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
4331
|
+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
4332
|
+
4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
|
|
4333
|
+
-----END PUBLIC KEY-----`;
|
|
4334
|
+
LICENSE_JWT_ALG = "ES256";
|
|
3901
4335
|
PLAN_LIMITS = {
|
|
3902
4336
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
3903
4337
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -3905,6 +4339,19 @@ var init_license = __esm({
|
|
|
3905
4339
|
agency: { devices: 50, employees: 100, memories: 1e7 },
|
|
3906
4340
|
enterprise: { devices: -1, employees: -1, memories: -1 }
|
|
3907
4341
|
};
|
|
4342
|
+
FREE_LICENSE = {
|
|
4343
|
+
valid: true,
|
|
4344
|
+
plan: "free",
|
|
4345
|
+
email: "",
|
|
4346
|
+
expiresAt: null,
|
|
4347
|
+
deviceLimit: 1,
|
|
4348
|
+
employeeLimit: 1,
|
|
4349
|
+
memoryLimit: 5e3
|
|
4350
|
+
};
|
|
4351
|
+
_prismaPromise = null;
|
|
4352
|
+
_prismaFailed = false;
|
|
4353
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
4354
|
+
_revalTimer = null;
|
|
3908
4355
|
}
|
|
3909
4356
|
});
|
|
3910
4357
|
|
|
@@ -5506,10 +5953,18 @@ async function storeBehavior(opts) {
|
|
|
5506
5953
|
vector = new Float32Array(vec);
|
|
5507
5954
|
} catch {
|
|
5508
5955
|
}
|
|
5956
|
+
let createdByDevice = null;
|
|
5957
|
+
try {
|
|
5958
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
5959
|
+
createdByDevice = loadDeviceId2() ?? null;
|
|
5960
|
+
} catch {
|
|
5961
|
+
}
|
|
5962
|
+
const createdByAgent = process.env.AGENT_ID ?? null;
|
|
5963
|
+
const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
|
|
5509
5964
|
await client.execute({
|
|
5510
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
5511
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
5512
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
5965
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
|
|
5966
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
|
|
5967
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
|
|
5513
5968
|
});
|
|
5514
5969
|
return id;
|
|
5515
5970
|
}
|
|
@@ -8374,7 +8829,7 @@ var init_platform_procedures = __esm({
|
|
|
8374
8829
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
8375
8830
|
domain: "tool-use",
|
|
8376
8831
|
priority: "p0",
|
|
8377
|
-
content: 'exe-os MCP tools
|
|
8832
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
8378
8833
|
},
|
|
8379
8834
|
{
|
|
8380
8835
|
title: "MCP tools \u2014 memory, decision, and search",
|
package/dist/bin/exe-doctor.js
CHANGED
|
@@ -3137,6 +3137,13 @@ async function ensureSchema() {
|
|
|
3137
3137
|
} catch (e) {
|
|
3138
3138
|
logCatchDebug("migration", e);
|
|
3139
3139
|
}
|
|
3140
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
3141
|
+
try {
|
|
3142
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
3143
|
+
} catch (e) {
|
|
3144
|
+
logCatchDebug("migration", e);
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3140
3147
|
try {
|
|
3141
3148
|
await client.execute({
|
|
3142
3149
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -4893,7 +4900,7 @@ var init_platform_procedures = __esm({
|
|
|
4893
4900
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
4894
4901
|
domain: "tool-use",
|
|
4895
4902
|
priority: "p0",
|
|
4896
|
-
content: 'exe-os MCP tools
|
|
4903
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
4897
4904
|
},
|
|
4898
4905
|
{
|
|
4899
4906
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -2102,6 +2102,13 @@ async function ensureSchema() {
|
|
|
2102
2102
|
} catch (e) {
|
|
2103
2103
|
logCatchDebug("migration", e);
|
|
2104
2104
|
}
|
|
2105
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2106
|
+
try {
|
|
2107
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2108
|
+
} catch (e) {
|
|
2109
|
+
logCatchDebug("migration", e);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2105
2112
|
try {
|
|
2106
2113
|
await client.execute({
|
|
2107
2114
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -4630,7 +4637,7 @@ var init_platform_procedures = __esm({
|
|
|
4630
4637
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
4631
4638
|
domain: "tool-use",
|
|
4632
4639
|
priority: "p0",
|
|
4633
|
-
content: 'exe-os MCP tools
|
|
4640
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
4634
4641
|
},
|
|
4635
4642
|
{
|
|
4636
4643
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -5925,7 +5932,7 @@ import crypto2 from "crypto";
|
|
|
5925
5932
|
async function listBehaviors(agentId2, projectName2, limit = 30) {
|
|
5926
5933
|
const client = getClient();
|
|
5927
5934
|
const result = await client.execute({
|
|
5928
|
-
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector
|
|
5935
|
+
sql: `SELECT id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id
|
|
5929
5936
|
FROM behaviors
|
|
5930
5937
|
WHERE agent_id = ? AND active = 1
|
|
5931
5938
|
AND (project_name IS NULL OR project_name = ?)
|
|
@@ -5954,7 +5961,10 @@ async function listBehaviors(agentId2, projectName2, limit = 30) {
|
|
|
5954
5961
|
active: Number(r.active),
|
|
5955
5962
|
created_at: String(r.created_at),
|
|
5956
5963
|
updated_at: String(r.updated_at),
|
|
5957
|
-
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null
|
|
5964
|
+
vector: r.vector ? Array.from(new Float32Array(r.vector)) : null,
|
|
5965
|
+
created_by_agent: r.created_by_agent ? String(r.created_by_agent) : null,
|
|
5966
|
+
created_by_device: r.created_by_device ? String(r.created_by_device) : null,
|
|
5967
|
+
source_session_id: r.source_session_id ? String(r.source_session_id) : null
|
|
5958
5968
|
}));
|
|
5959
5969
|
}
|
|
5960
5970
|
|
package/dist/bin/exe-forget.js
CHANGED
|
@@ -2026,6 +2026,13 @@ async function ensureSchema() {
|
|
|
2026
2026
|
} catch (e) {
|
|
2027
2027
|
logCatchDebug("migration", e);
|
|
2028
2028
|
}
|
|
2029
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2030
|
+
try {
|
|
2031
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2032
|
+
} catch (e) {
|
|
2033
|
+
logCatchDebug("migration", e);
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2029
2036
|
try {
|
|
2030
2037
|
await client.execute({
|
|
2031
2038
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -4554,7 +4561,7 @@ var init_platform_procedures = __esm({
|
|
|
4554
4561
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
4555
4562
|
domain: "tool-use",
|
|
4556
4563
|
priority: "p0",
|
|
4557
|
-
content: 'exe-os MCP tools
|
|
4564
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
4558
4565
|
},
|
|
4559
4566
|
{
|
|
4560
4567
|
title: "MCP tools \u2014 memory, decision, and search",
|
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -2697,6 +2697,13 @@ async function ensureSchema() {
|
|
|
2697
2697
|
} catch (e) {
|
|
2698
2698
|
logCatchDebug("migration", e);
|
|
2699
2699
|
}
|
|
2700
|
+
for (const col of ["created_by_agent TEXT", "created_by_device TEXT", "source_session_id TEXT"]) {
|
|
2701
|
+
try {
|
|
2702
|
+
await client.execute({ sql: `ALTER TABLE behaviors ADD COLUMN ${col}`, args: [] });
|
|
2703
|
+
} catch (e) {
|
|
2704
|
+
logCatchDebug("migration", e);
|
|
2705
|
+
}
|
|
2706
|
+
}
|
|
2700
2707
|
try {
|
|
2701
2708
|
await client.execute({
|
|
2702
2709
|
sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
|
|
@@ -5238,7 +5245,7 @@ var init_platform_procedures = __esm({
|
|
|
5238
5245
|
title: "MCP tool dispatch \u2014 all tools use action parameter",
|
|
5239
5246
|
domain: "tool-use",
|
|
5240
5247
|
priority: "p0",
|
|
5241
|
-
content: 'exe-os MCP tools
|
|
5248
|
+
content: 'exe-os MCP tools use consolidated action-based dispatch by default (19 tools). Call domain tools with an action parameter: memory(action="recall"), task(action="create"), config(action="list_employees"), etc. Legacy mode (108 separate tools like recall_my_memory, create_task) is still available via EXE_MCP_TOOL_SURFACE=legacy but will be removed in a future version. If you see specific tool names, call them directly \u2014 both surfaces are identical. Consolidated is the default and recommended surface.'
|
|
5242
5249
|
},
|
|
5243
5250
|
{
|
|
5244
5251
|
title: "MCP tools \u2014 memory, decision, and search",
|
|
@@ -11669,10 +11676,18 @@ async function storeBehavior(opts) {
|
|
|
11669
11676
|
vector = new Float32Array(vec);
|
|
11670
11677
|
} catch {
|
|
11671
11678
|
}
|
|
11679
|
+
let createdByDevice = null;
|
|
11680
|
+
try {
|
|
11681
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
11682
|
+
createdByDevice = loadDeviceId2() ?? null;
|
|
11683
|
+
} catch {
|
|
11684
|
+
}
|
|
11685
|
+
const createdByAgent = process.env.AGENT_ID ?? null;
|
|
11686
|
+
const sourceSessionId = process.env.CLAUDE_SESSION_ID ?? process.env.SESSION_ID ?? null;
|
|
11672
11687
|
await client.execute({
|
|
11673
|
-
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
|
|
11674
|
-
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
|
|
11675
|
-
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
|
|
11688
|
+
sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector, created_by_agent, created_by_device, source_session_id)
|
|
11689
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?)`,
|
|
11690
|
+
args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null, createdByAgent, createdByDevice, sourceSessionId]
|
|
11676
11691
|
});
|
|
11677
11692
|
return id;
|
|
11678
11693
|
}
|