@askexenow/exe-os 0.8.37 → 0.8.38
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/backfill-conversations.js +66 -60
- package/dist/bin/backfill-responses.js +7 -8
- package/dist/bin/backfill-vectors.js +1 -8
- package/dist/bin/cleanup-stale-review-tasks.js +1 -8
- package/dist/bin/cli.js +520 -325
- package/dist/bin/exe-assign.js +7 -8
- package/dist/bin/exe-boot.js +54 -21
- package/dist/bin/exe-call.js +9 -4
- package/dist/bin/exe-cloud.js +37 -3
- package/dist/bin/exe-doctor.js +1 -8
- package/dist/bin/exe-export-behaviors.js +4 -11
- package/dist/bin/exe-forget.js +1 -8
- package/dist/bin/exe-gateway.js +72 -30
- package/dist/bin/exe-heartbeat.js +4 -11
- package/dist/bin/exe-kill.js +1 -8
- package/dist/bin/exe-launch-agent.js +51 -14
- package/dist/bin/exe-link.js +13 -3
- package/dist/bin/exe-new-employee.js +35 -10
- package/dist/bin/exe-pending-messages.js +1 -8
- package/dist/bin/exe-pending-notifications.js +1 -8
- package/dist/bin/exe-pending-reviews.js +4 -11
- package/dist/bin/exe-review.js +7 -8
- package/dist/bin/exe-search.js +10 -11
- package/dist/bin/exe-session-cleanup.js +11 -12
- package/dist/bin/exe-status.js +1 -8
- package/dist/bin/exe-team.js +1 -8
- package/dist/bin/git-sweep.js +7 -8
- package/dist/bin/graph-backfill.js +1 -8
- package/dist/bin/graph-export.js +1 -8
- package/dist/bin/install.js +9 -0
- package/dist/bin/scan-tasks.js +7 -8
- package/dist/bin/setup.js +396 -245
- package/dist/bin/shard-migrate.js +1 -8
- package/dist/bin/wiki-sync.js +1 -8
- package/dist/gateway/index.js +30 -30
- package/dist/hooks/bug-report-worker.js +4 -11
- package/dist/hooks/commit-complete.js +7 -8
- package/dist/hooks/error-recall.js +11 -12
- package/dist/hooks/ingest-worker.js +24 -9
- package/dist/hooks/instructions-loaded.js +7 -8
- package/dist/hooks/notification.js +7 -8
- package/dist/hooks/post-compact.js +7 -8
- package/dist/hooks/pre-compact.js +7 -8
- package/dist/hooks/pre-tool-use.js +7 -8
- package/dist/hooks/prompt-ingest-worker.js +19 -4
- package/dist/hooks/prompt-submit.js +14 -9
- package/dist/hooks/response-ingest-worker.js +20 -5
- package/dist/hooks/session-end.js +11 -12
- package/dist/hooks/session-start.js +11 -12
- package/dist/hooks/stop.js +7 -8
- package/dist/hooks/subagent-stop.js +7 -8
- package/dist/hooks/summary-worker.js +24 -9
- package/dist/index.js +11 -5
- package/dist/lib/cloud-sync.js +19 -2
- package/dist/lib/employee-templates.js +5 -0
- package/dist/lib/exe-daemon.js +24 -8
- package/dist/lib/hybrid-search.js +10 -11
- package/dist/lib/identity-templates.js +16 -7
- package/dist/lib/license.js +43 -2
- package/dist/lib/schedules.js +1 -8
- package/dist/lib/store.js +7 -8
- package/dist/mcp/server.js +184 -113
- package/dist/mcp/tools/list-tasks.js +35 -27
- package/dist/runtime/index.js +7 -2
- package/dist/tui/App.js +44 -5
- package/package.json +4 -2
package/dist/bin/setup.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -678,6 +684,8 @@ __export(license_exports, {
|
|
|
678
684
|
loadLicense: () => loadLicense,
|
|
679
685
|
mirrorLicenseKey: () => mirrorLicenseKey,
|
|
680
686
|
saveLicense: () => saveLicense,
|
|
687
|
+
startLicenseRevalidation: () => startLicenseRevalidation,
|
|
688
|
+
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
681
689
|
validateLicense: () => validateLicense
|
|
682
690
|
});
|
|
683
691
|
import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync5, mkdirSync } from "fs";
|
|
@@ -802,14 +810,23 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
802
810
|
} catch {
|
|
803
811
|
const cached = await getCachedLicense();
|
|
804
812
|
if (cached) return cached;
|
|
805
|
-
return FREE_LICENSE;
|
|
813
|
+
return { ...FREE_LICENSE, valid: false, error: "offline" };
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function getCacheAgeMs() {
|
|
817
|
+
try {
|
|
818
|
+
const { statSync: statSync2 } = __require("fs");
|
|
819
|
+
const s = statSync2(CACHE_PATH);
|
|
820
|
+
return Date.now() - s.mtimeMs;
|
|
821
|
+
} catch {
|
|
822
|
+
return Infinity;
|
|
806
823
|
}
|
|
807
824
|
}
|
|
808
825
|
async function checkLicense() {
|
|
809
826
|
const key = loadLicense();
|
|
810
827
|
if (!key) return FREE_LICENSE;
|
|
811
828
|
const cached = await getCachedLicense();
|
|
812
|
-
if (cached) return cached;
|
|
829
|
+
if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
|
|
813
830
|
const deviceId = loadDeviceId();
|
|
814
831
|
return validateLicense(key, deviceId);
|
|
815
832
|
}
|
|
@@ -930,7 +947,28 @@ async function assertVpsLicense(opts) {
|
|
|
930
947
|
`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.`
|
|
931
948
|
);
|
|
932
949
|
}
|
|
933
|
-
|
|
950
|
+
function startLicenseRevalidation(intervalMs = 36e5) {
|
|
951
|
+
if (_revalTimer) return;
|
|
952
|
+
_revalTimer = setInterval(async () => {
|
|
953
|
+
try {
|
|
954
|
+
const license = await checkLicense();
|
|
955
|
+
if (!license.valid) {
|
|
956
|
+
process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
|
|
957
|
+
}
|
|
958
|
+
} catch {
|
|
959
|
+
}
|
|
960
|
+
}, intervalMs);
|
|
961
|
+
if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
|
|
962
|
+
_revalTimer.unref();
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
function stopLicenseRevalidation() {
|
|
966
|
+
if (_revalTimer) {
|
|
967
|
+
clearInterval(_revalTimer);
|
|
968
|
+
_revalTimer = null;
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
|
|
934
972
|
var init_license = __esm({
|
|
935
973
|
"src/lib/license.ts"() {
|
|
936
974
|
"use strict";
|
|
@@ -960,6 +998,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
960
998
|
employeeLimit: 1,
|
|
961
999
|
memoryLimit: 5e3
|
|
962
1000
|
};
|
|
1001
|
+
CACHE_MAX_AGE_MS = 36e5;
|
|
1002
|
+
_revalTimer = null;
|
|
963
1003
|
}
|
|
964
1004
|
});
|
|
965
1005
|
|
|
@@ -1106,6 +1146,7 @@ __export(employee_templates_exports, {
|
|
|
1106
1146
|
buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
|
|
1107
1147
|
getSessionPrompt: () => getSessionPrompt,
|
|
1108
1148
|
getTemplate: () => getTemplate,
|
|
1149
|
+
getTemplateByRole: () => getTemplateByRole,
|
|
1109
1150
|
personalizePrompt: () => personalizePrompt,
|
|
1110
1151
|
renderClientCOOTemplate: () => renderClientCOOTemplate
|
|
1111
1152
|
});
|
|
@@ -1121,6 +1162,10 @@ function buildCustomEmployeePrompt(name, role) {
|
|
|
1121
1162
|
function getTemplate(name) {
|
|
1122
1163
|
return TEMPLATES[name];
|
|
1123
1164
|
}
|
|
1165
|
+
function getTemplateByRole(role) {
|
|
1166
|
+
const lower = role.toLowerCase();
|
|
1167
|
+
return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
|
|
1168
|
+
}
|
|
1124
1169
|
function personalizePrompt(prompt, templateName, actualName) {
|
|
1125
1170
|
if (templateName === actualName) return prompt;
|
|
1126
1171
|
const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1868,6 +1913,7 @@ var init_identity = __esm({
|
|
|
1868
1913
|
var identity_templates_exports = {};
|
|
1869
1914
|
__export(identity_templates_exports, {
|
|
1870
1915
|
IDENTITY_TEMPLATES: () => IDENTITY_TEMPLATES,
|
|
1916
|
+
PLAN_MODE_COMPAT: () => PLAN_MODE_COMPAT,
|
|
1871
1917
|
POST_WORK_CHECKLIST: () => POST_WORK_CHECKLIST,
|
|
1872
1918
|
getTemplate: () => getTemplate2,
|
|
1873
1919
|
getTemplateForTitle: () => getTemplateForTitle
|
|
@@ -1887,10 +1933,18 @@ function getTemplateForTitle(title) {
|
|
|
1887
1933
|
if (t.includes("review") || t.includes("audit") || t.includes("qa")) return IDENTITY_TEMPLATES["staff-code-reviewer"];
|
|
1888
1934
|
return null;
|
|
1889
1935
|
}
|
|
1890
|
-
var POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
1936
|
+
var PLAN_MODE_COMPAT, POST_WORK_CHECKLIST, IDENTITY_TEMPLATES;
|
|
1891
1937
|
var init_identity_templates = __esm({
|
|
1892
1938
|
"src/lib/identity-templates.ts"() {
|
|
1893
1939
|
"use strict";
|
|
1940
|
+
PLAN_MODE_COMPAT = `
|
|
1941
|
+
## Plan Mode Compatibility
|
|
1942
|
+
If tool execution is unavailable (e.g., CC plan mode), switch to planning:
|
|
1943
|
+
- Reason about the task and create a written plan
|
|
1944
|
+
- Document what tools you would call and with what parameters
|
|
1945
|
+
- Output structured text that can be acted on when tools become available
|
|
1946
|
+
Do not repeatedly attempt tool calls that fail \u2014 switch to planning mode.
|
|
1947
|
+
`;
|
|
1894
1948
|
POST_WORK_CHECKLIST = `
|
|
1895
1949
|
5. Check for pending reviews (list_tasks status='needs_review' where you are reviewer) \u2014 reviews are work, process before new tasks
|
|
1896
1950
|
6. Check for blocked tasks (list_tasks status='blocked') \u2014 can you unblock it? Do it now. Can't? Escalate to exe immediately.
|
|
@@ -1970,7 +2024,7 @@ Never say "I have no memories" without first searching broadly. Your memory may
|
|
|
1970
2024
|
- **update_identity** \u2014 rewrite any agent's identity when role/responsibilities change (exe/founder only)
|
|
1971
2025
|
- **get_identity** \u2014 read any agent's identity for coordination
|
|
1972
2026
|
- **send_message** \u2014 direct intercom to employees
|
|
1973
|
-
|
|
2027
|
+
${PLAN_MODE_COMPAT}
|
|
1974
2028
|
## Completion Workflow
|
|
1975
2029
|
|
|
1976
2030
|
1. Read the task file and verify the deliverable matches the brief
|
|
@@ -2041,7 +2095,7 @@ You are \${agent_id}. CTO. You hold deep context on the entire codebase, archite
|
|
|
2041
2095
|
- **store_behavior** \u2014 record corrections for engineers (p0 = always injected)
|
|
2042
2096
|
- **get_identity** \u2014 read any agent's identity for review context
|
|
2043
2097
|
- **query_relationships** \u2014 GraphRAG entity connections for architecture analysis
|
|
2044
|
-
|
|
2098
|
+
${PLAN_MODE_COMPAT}
|
|
2045
2099
|
## Completion Workflow
|
|
2046
2100
|
|
|
2047
2101
|
1. Read ARCHITECTURE.md before starting work on any repo
|
|
@@ -2108,7 +2162,7 @@ You are \${agent_id}. CMO. You hold deep context on design, branding, storytelli
|
|
|
2108
2162
|
- **update_task** \u2014 mark tasks done with result summary
|
|
2109
2163
|
- **store_memory** \u2014 report completions with brand alignment notes, SEO considerations
|
|
2110
2164
|
- **get_identity** \u2014 read team identities for brand-consistent communication
|
|
2111
|
-
|
|
2165
|
+
${PLAN_MODE_COMPAT}
|
|
2112
2166
|
## Completion Workflow
|
|
2113
2167
|
|
|
2114
2168
|
1. Read the task file and understand the brief \u2014 tone, format, channel requirements
|
|
@@ -2175,7 +2229,7 @@ You are a principal engineer. You write production-grade code with zero shortcut
|
|
|
2175
2229
|
- **recall_my_memory** \u2014 check past work, patterns, gotchas in this project
|
|
2176
2230
|
- **store_memory** \u2014 report completions for org visibility
|
|
2177
2231
|
- **ask_team_memory** \u2014 pull context from colleagues when specs reference their work
|
|
2178
|
-
|
|
2232
|
+
${PLAN_MODE_COMPAT}
|
|
2179
2233
|
## Completion Workflow
|
|
2180
2234
|
|
|
2181
2235
|
1. Read ARCHITECTURE.md if it exists \u2014 understand architecture before changing anything
|
|
@@ -2235,7 +2289,7 @@ You are the content production specialist. You turn scripts and creative briefs
|
|
|
2235
2289
|
- **update_task** \u2014 mark tasks done with result summary
|
|
2236
2290
|
- **recall_my_memory** \u2014 check past work: which models worked, which prompts produced good results
|
|
2237
2291
|
- **store_memory** \u2014 report completions with production decisions for future reference
|
|
2238
|
-
|
|
2292
|
+
${PLAN_MODE_COMPAT}
|
|
2239
2293
|
## Completion Workflow
|
|
2240
2294
|
|
|
2241
2295
|
1. Read the task file \u2014 understand the brief, check budget constraints
|
|
@@ -2307,7 +2361,7 @@ You are the AI Product Lead \u2014 the competitive intelligence engine. You stud
|
|
|
2307
2361
|
- **update_task** \u2014 mark tasks done with analysis results
|
|
2308
2362
|
- **store_memory** \u2014 persist competitive analyses, evaluations, recommendations
|
|
2309
2363
|
- **create_task** \u2014 when a feature is worth building, spec it for the CTO
|
|
2310
|
-
|
|
2364
|
+
${PLAN_MODE_COMPAT}
|
|
2311
2365
|
## Completion Workflow
|
|
2312
2366
|
|
|
2313
2367
|
1. Read the task \u2014 understand what capability is needed
|
|
@@ -2370,7 +2424,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
2370
2424
|
- **store_behavior** \u2014 record new patterns
|
|
2371
2425
|
- **update_task** \u2014 mark reviews done with structured findings
|
|
2372
2426
|
- **create_task** \u2014 assign fixes to the CTO
|
|
2373
|
-
|
|
2427
|
+
${PLAN_MODE_COMPAT}
|
|
2374
2428
|
## Completion Workflow
|
|
2375
2429
|
|
|
2376
2430
|
1. Read the task brief and understand the audit scope
|
|
@@ -2388,7 +2442,7 @@ You are \${agent_id}. Staff Code Reviewer and System Auditor. Last line of defen
|
|
|
2388
2442
|
// src/lib/setup-wizard.ts
|
|
2389
2443
|
init_config();
|
|
2390
2444
|
import crypto2 from "crypto";
|
|
2391
|
-
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
|
|
2445
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3 } from "fs";
|
|
2392
2446
|
import os2 from "os";
|
|
2393
2447
|
import path8 from "path";
|
|
2394
2448
|
import { createInterface } from "readline";
|
|
@@ -2467,48 +2521,67 @@ async function downloadModel(opts) {
|
|
|
2467
2521
|
const tmpPath = destPath + ".tmp";
|
|
2468
2522
|
await mkdir3(destDir, { recursive: true });
|
|
2469
2523
|
if (existsSync3(destPath)) {
|
|
2470
|
-
const
|
|
2471
|
-
if (
|
|
2524
|
+
const hash = await fileHash(destPath);
|
|
2525
|
+
if (hash === EXPECTED_SHA256) {
|
|
2472
2526
|
return destPath;
|
|
2473
2527
|
}
|
|
2474
2528
|
}
|
|
2475
|
-
|
|
2476
|
-
const
|
|
2477
|
-
|
|
2478
|
-
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
2479
|
-
}
|
|
2480
|
-
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
2529
|
+
const MAX_RETRIES = 3;
|
|
2530
|
+
const DOWNLOAD_TIMEOUT_MS = 3e5;
|
|
2531
|
+
let lastErr;
|
|
2481
2532
|
let downloaded = 0;
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
if (!
|
|
2490
|
-
|
|
2533
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
2534
|
+
try {
|
|
2535
|
+
if (existsSync3(tmpPath)) unlinkSync(tmpPath);
|
|
2536
|
+
const response = await fetchFn(GGUF_URL, {
|
|
2537
|
+
redirect: "follow",
|
|
2538
|
+
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS)
|
|
2539
|
+
});
|
|
2540
|
+
if (!response.ok || !response.body) {
|
|
2541
|
+
throw new Error(`Download failed: HTTP ${response.status}`);
|
|
2542
|
+
}
|
|
2543
|
+
const contentLength = Number(response.headers.get("content-length") ?? EXPECTED_SIZE);
|
|
2544
|
+
const hash = createHash("sha256");
|
|
2545
|
+
const fileStream = createWriteStream(tmpPath);
|
|
2546
|
+
const reader = response.body.getReader();
|
|
2547
|
+
try {
|
|
2548
|
+
while (true) {
|
|
2549
|
+
const { done, value } = await reader.read();
|
|
2550
|
+
if (done) break;
|
|
2551
|
+
if (!fileStream.write(value)) {
|
|
2552
|
+
await new Promise((resolve) => fileStream.once("drain", resolve));
|
|
2553
|
+
}
|
|
2554
|
+
hash.update(value);
|
|
2555
|
+
downloaded += value.byteLength;
|
|
2556
|
+
onProgress?.(downloaded, contentLength);
|
|
2557
|
+
}
|
|
2558
|
+
} finally {
|
|
2559
|
+
fileStream.end();
|
|
2560
|
+
await new Promise((resolve, reject) => {
|
|
2561
|
+
fileStream.on("finish", resolve);
|
|
2562
|
+
fileStream.on("error", reject);
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
const actualHash = hash.digest("hex");
|
|
2566
|
+
if (actualHash !== EXPECTED_SHA256) {
|
|
2567
|
+
unlinkSync(tmpPath);
|
|
2568
|
+
throw new Error(
|
|
2569
|
+
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
2570
|
+
);
|
|
2571
|
+
}
|
|
2572
|
+
renameSync2(tmpPath, destPath);
|
|
2573
|
+
return destPath;
|
|
2574
|
+
} catch (err) {
|
|
2575
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
2576
|
+
if (attempt < MAX_RETRIES) {
|
|
2577
|
+
process.stderr.write(`
|
|
2578
|
+
Download attempt ${attempt} failed, retrying...
|
|
2579
|
+
`);
|
|
2580
|
+
if (existsSync3(tmpPath)) unlinkSync(tmpPath);
|
|
2491
2581
|
}
|
|
2492
|
-
hash.update(value);
|
|
2493
|
-
downloaded += value.byteLength;
|
|
2494
|
-
onProgress?.(downloaded, contentLength);
|
|
2495
2582
|
}
|
|
2496
|
-
} finally {
|
|
2497
|
-
fileStream.end();
|
|
2498
|
-
await new Promise((resolve, reject) => {
|
|
2499
|
-
fileStream.on("finish", resolve);
|
|
2500
|
-
fileStream.on("error", reject);
|
|
2501
|
-
});
|
|
2502
2583
|
}
|
|
2503
|
-
|
|
2504
|
-
if (actualHash !== EXPECTED_SHA256) {
|
|
2505
|
-
unlinkSync(tmpPath);
|
|
2506
|
-
throw new Error(
|
|
2507
|
-
`SHA256 mismatch: expected ${EXPECTED_SHA256}, got ${actualHash}`
|
|
2508
|
-
);
|
|
2509
|
-
}
|
|
2510
|
-
renameSync2(tmpPath, destPath);
|
|
2511
|
-
return destPath;
|
|
2584
|
+
throw lastErr;
|
|
2512
2585
|
}
|
|
2513
2586
|
async function fileHash(filePath) {
|
|
2514
2587
|
return new Promise((resolve, reject) => {
|
|
@@ -2521,6 +2594,24 @@ async function fileHash(filePath) {
|
|
|
2521
2594
|
}
|
|
2522
2595
|
|
|
2523
2596
|
// src/lib/setup-wizard.ts
|
|
2597
|
+
var SETUP_STATE_PATH = path8.join(os2.homedir(), ".exe-os", "setup-state.json");
|
|
2598
|
+
function loadSetupState() {
|
|
2599
|
+
try {
|
|
2600
|
+
return JSON.parse(readFileSync6(SETUP_STATE_PATH, "utf8"));
|
|
2601
|
+
} catch {
|
|
2602
|
+
return { completedSteps: [], startedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
function saveSetupState(state) {
|
|
2606
|
+
mkdirSync3(path8.dirname(SETUP_STATE_PATH), { recursive: true });
|
|
2607
|
+
writeFileSync3(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
|
|
2608
|
+
}
|
|
2609
|
+
function clearSetupState() {
|
|
2610
|
+
try {
|
|
2611
|
+
unlinkSync3(SETUP_STATE_PATH);
|
|
2612
|
+
} catch {
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2524
2615
|
function ask(rl, prompt) {
|
|
2525
2616
|
return new Promise((resolve) => {
|
|
2526
2617
|
const doAsk = () => {
|
|
@@ -2560,88 +2651,122 @@ async function runSetupWizard(opts = {}) {
|
|
|
2560
2651
|
rl.close();
|
|
2561
2652
|
return;
|
|
2562
2653
|
}
|
|
2654
|
+
const state = loadSetupState();
|
|
2655
|
+
if (state.completedSteps.length > 0) {
|
|
2656
|
+
log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
|
|
2657
|
+
}
|
|
2563
2658
|
if (existsSync8(LEGACY_LANCE_PATH)) {
|
|
2564
2659
|
log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
|
|
2565
2660
|
log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
|
|
2566
2661
|
log(" The old directory will not be modified or deleted.");
|
|
2567
2662
|
log("");
|
|
2568
2663
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2664
|
+
if (!state.completedSteps.includes(1)) {
|
|
2665
|
+
const existingKey = await getMasterKey();
|
|
2666
|
+
if (existingKey) {
|
|
2667
|
+
log("Encryption key already exists \u2014 skipping generation.");
|
|
2668
|
+
} else {
|
|
2669
|
+
log("Generating 256-bit encryption key...");
|
|
2670
|
+
const key = crypto2.randomBytes(32);
|
|
2671
|
+
await setMasterKey(key);
|
|
2672
|
+
log("Encryption key generated and stored securely.");
|
|
2673
|
+
}
|
|
2674
|
+
state.completedSteps.push(1);
|
|
2675
|
+
saveSetupState(state);
|
|
2572
2676
|
} else {
|
|
2573
|
-
log("
|
|
2574
|
-
const key = crypto2.randomBytes(32);
|
|
2575
|
-
await setMasterKey(key);
|
|
2576
|
-
log("Encryption key generated and stored securely.");
|
|
2677
|
+
log("Step 1 already complete \u2014 skipping.");
|
|
2577
2678
|
}
|
|
2578
2679
|
log("");
|
|
2579
|
-
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
2580
|
-
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
2581
|
-
log("only your encryption key can decrypt it.");
|
|
2582
2680
|
let cloudConfig;
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2681
|
+
if (!state.completedSteps.includes(2)) {
|
|
2682
|
+
log("Exe Cloud: your memories are end-to-end encrypted, compressed, and");
|
|
2683
|
+
log("backed up on Exe Cloud. Free for all plans. We can't read your data \u2014");
|
|
2684
|
+
log("only your encryption key can decrypt it.");
|
|
2685
|
+
try {
|
|
2686
|
+
const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2687
|
+
const deviceId = loadDeviceId2();
|
|
2688
|
+
const res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
|
|
2689
|
+
method: "POST",
|
|
2690
|
+
headers: { "Content-Type": "application/json" },
|
|
2691
|
+
body: JSON.stringify({ deviceId }),
|
|
2692
|
+
signal: AbortSignal.timeout(1e4)
|
|
2693
|
+
});
|
|
2694
|
+
if (res.ok) {
|
|
2695
|
+
const data = await res.json();
|
|
2696
|
+
if (data.apiKey) {
|
|
2697
|
+
cloudConfig = { apiKey: data.apiKey, endpoint: "https://askexe.com/cloud" };
|
|
2698
|
+
const { saveLicense: saveLicense3, mirrorLicenseKey: mirrorLicenseKey3 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2699
|
+
saveLicense3(data.apiKey);
|
|
2700
|
+
mirrorLicenseKey3(data.apiKey);
|
|
2701
|
+
log("Cloud sync activated automatically.");
|
|
2702
|
+
}
|
|
2600
2703
|
}
|
|
2704
|
+
} catch {
|
|
2705
|
+
log("Cloud sync will activate when online.");
|
|
2601
2706
|
}
|
|
2602
|
-
|
|
2603
|
-
|
|
2707
|
+
state.completedSteps.push(2);
|
|
2708
|
+
saveSetupState(state);
|
|
2709
|
+
} else {
|
|
2710
|
+
log("Step 2 already complete \u2014 skipping.");
|
|
2604
2711
|
}
|
|
2605
2712
|
log("");
|
|
2606
|
-
if (!
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2713
|
+
if (!state.completedSteps.includes(3)) {
|
|
2714
|
+
if (!skipModel2) {
|
|
2715
|
+
log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
|
|
2716
|
+
log("");
|
|
2717
|
+
await downloadModel({
|
|
2718
|
+
destDir: MODELS_DIR,
|
|
2719
|
+
onProgress: (downloaded, total) => {
|
|
2720
|
+
const pct = (downloaded / total * 100).toFixed(1);
|
|
2721
|
+
const dlMB = (downloaded / 1e6).toFixed(0);
|
|
2722
|
+
const totalMB = (total / 1e6).toFixed(0);
|
|
2723
|
+
process.stderr.write(`\rDownloading model: ${pct}% (${dlMB}/${totalMB} MB)`);
|
|
2724
|
+
}
|
|
2725
|
+
});
|
|
2726
|
+
process.stderr.write("\n");
|
|
2727
|
+
log("Model downloaded and verified.");
|
|
2728
|
+
}
|
|
2729
|
+
state.completedSteps.push(3);
|
|
2730
|
+
saveSetupState(state);
|
|
2731
|
+
} else {
|
|
2732
|
+
log("Step 3 already complete \u2014 skipping.");
|
|
2620
2733
|
}
|
|
2621
|
-
if (!
|
|
2622
|
-
|
|
2734
|
+
if (!state.completedSteps.includes(4)) {
|
|
2735
|
+
if (!skipModel2 && !skipModelValidation) {
|
|
2736
|
+
await validateModel(log);
|
|
2737
|
+
}
|
|
2738
|
+
state.completedSteps.push(4);
|
|
2739
|
+
saveSetupState(state);
|
|
2740
|
+
} else {
|
|
2741
|
+
log("Step 4 already complete \u2014 skipping.");
|
|
2623
2742
|
}
|
|
2624
2743
|
const config = await loadConfig();
|
|
2625
|
-
if (
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
const claudeJsonPath = path8.join(os2.homedir(), ".claude.json");
|
|
2632
|
-
let claudeJson = {};
|
|
2744
|
+
if (!state.completedSteps.includes(5)) {
|
|
2745
|
+
if (cloudConfig) {
|
|
2746
|
+
config.cloud = cloudConfig;
|
|
2747
|
+
}
|
|
2748
|
+
await saveConfig(config);
|
|
2749
|
+
log("");
|
|
2633
2750
|
try {
|
|
2634
|
-
|
|
2751
|
+
const claudeJsonPath = path8.join(os2.homedir(), ".claude.json");
|
|
2752
|
+
let claudeJson = {};
|
|
2753
|
+
try {
|
|
2754
|
+
claudeJson = JSON.parse(readFileSync6(claudeJsonPath, "utf8"));
|
|
2755
|
+
} catch {
|
|
2756
|
+
}
|
|
2757
|
+
if (!claudeJson.projects) claudeJson.projects = {};
|
|
2758
|
+
const projects = claudeJson.projects;
|
|
2759
|
+
for (const dir of [process.cwd(), os2.homedir()]) {
|
|
2760
|
+
if (!projects[dir]) projects[dir] = {};
|
|
2761
|
+
projects[dir].hasTrustDialogAccepted = true;
|
|
2762
|
+
}
|
|
2763
|
+
writeFileSync3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
2635
2764
|
} catch {
|
|
2636
2765
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
projects[dir].hasTrustDialogAccepted = true;
|
|
2642
|
-
}
|
|
2643
|
-
writeFileSync3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
2644
|
-
} catch {
|
|
2766
|
+
state.completedSteps.push(5);
|
|
2767
|
+
saveSetupState(state);
|
|
2768
|
+
} else {
|
|
2769
|
+
log("Step 5 already complete \u2014 skipping.");
|
|
2645
2770
|
}
|
|
2646
2771
|
const {
|
|
2647
2772
|
loadEmployees: loadEmployees2,
|
|
@@ -2650,7 +2775,7 @@ async function runSetupWizard(opts = {}) {
|
|
|
2650
2775
|
registerBinSymlinks: registerBinSymlinks2,
|
|
2651
2776
|
EMPLOYEES_PATH: EMPLOYEES_PATH2
|
|
2652
2777
|
} = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
2653
|
-
const {
|
|
2778
|
+
const { getTemplateByRole: getTemplateByRole2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2654
2779
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
2655
2780
|
const { getTemplate: getIdentityTemplate } = await Promise.resolve().then(() => (init_identity_templates(), identity_templates_exports));
|
|
2656
2781
|
const {
|
|
@@ -2660,152 +2785,178 @@ async function runSetupWizard(opts = {}) {
|
|
|
2660
2785
|
validateLicense: validateLicense2
|
|
2661
2786
|
} = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
2662
2787
|
const createdEmployees = [];
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
log("They hold the big picture: priorities, progress, and blockers.");
|
|
2667
|
-
log("You talk to them. They coordinate everyone else.");
|
|
2668
|
-
log("");
|
|
2669
|
-
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
2670
|
-
const cooName = (cooNameInput || "exe").toLowerCase();
|
|
2671
|
-
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2672
|
-
if (!employees.some((e) => e.name === cooName)) {
|
|
2673
|
-
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2674
|
-
const cooEmployee = {
|
|
2675
|
-
name: cooName,
|
|
2676
|
-
role: "COO",
|
|
2677
|
-
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
2678
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2679
|
-
templateName: "exe",
|
|
2680
|
-
templateVersion: 1
|
|
2681
|
-
};
|
|
2682
|
-
employees = addEmployee2(employees, cooEmployee);
|
|
2683
|
-
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2684
|
-
}
|
|
2685
|
-
const cooIdentityContent = getIdentityTemplate("coo");
|
|
2686
|
-
if (cooIdentityContent) {
|
|
2687
|
-
const cooIdPath = identityPath2(cooName);
|
|
2688
|
-
mkdirSync3(path8.dirname(cooIdPath), { recursive: true });
|
|
2689
|
-
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
2690
|
-
writeFileSync3(cooIdPath, replaced, "utf-8");
|
|
2691
|
-
}
|
|
2692
|
-
registerBinSymlinks2(cooName);
|
|
2693
|
-
createdEmployees.push({ name: cooName, role: "COO" });
|
|
2694
|
-
log("");
|
|
2695
|
-
log("=== Meet Your Specialists ===");
|
|
2696
|
-
log("");
|
|
2697
|
-
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
2698
|
-
log("");
|
|
2699
|
-
log(" CTO (default: yoshi)");
|
|
2700
|
-
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
2701
|
-
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
2702
|
-
log("");
|
|
2703
|
-
log(" CMO (default: mari)");
|
|
2704
|
-
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
2705
|
-
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
2706
|
-
log("");
|
|
2707
|
-
log("Why separate roles instead of one AI that does everything?");
|
|
2708
|
-
log("");
|
|
2709
|
-
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
2710
|
-
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
2711
|
-
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
2712
|
-
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
2713
|
-
log("Your COO connects them so nothing falls through the cracks.");
|
|
2714
|
-
log("");
|
|
2715
|
-
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
2716
|
-
log("instead of headcount.");
|
|
2717
|
-
log("");
|
|
2718
|
-
await ask(rl, "Press Enter to continue. ");
|
|
2719
|
-
let license;
|
|
2720
|
-
try {
|
|
2721
|
-
license = await checkLicense2();
|
|
2722
|
-
} catch {
|
|
2723
|
-
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
2724
|
-
}
|
|
2725
|
-
if (license.plan === "free") {
|
|
2726
|
-
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
2788
|
+
let cooName = "exe";
|
|
2789
|
+
if (!state.completedSteps.includes(6)) {
|
|
2790
|
+
log("=== Your Team ===");
|
|
2727
2791
|
log("");
|
|
2728
|
-
log("
|
|
2729
|
-
log("
|
|
2730
|
-
log("
|
|
2792
|
+
log("Every install starts with a COO \u2014 your right-hand operator.");
|
|
2793
|
+
log("They hold the big picture: priorities, progress, and blockers.");
|
|
2794
|
+
log("You talk to them. They coordinate everyone else.");
|
|
2731
2795
|
log("");
|
|
2732
|
-
const
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2796
|
+
const cooNameInput = await ask(rl, "Name your COO (default: exe): ");
|
|
2797
|
+
cooName = (cooNameInput || "exe").toLowerCase();
|
|
2798
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2799
|
+
if (!employees.some((e) => e.name === cooName)) {
|
|
2800
|
+
const { DEFAULT_EXE: DEFAULT_EXE2, personalizePrompt: personalizePrompt2 } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2801
|
+
const cooEmployee = {
|
|
2802
|
+
name: cooName,
|
|
2803
|
+
role: "COO",
|
|
2804
|
+
systemPrompt: personalizePrompt2(DEFAULT_EXE2.systemPrompt, "exe", cooName),
|
|
2805
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2806
|
+
templateName: "exe",
|
|
2807
|
+
templateVersion: 1
|
|
2808
|
+
};
|
|
2809
|
+
employees = addEmployee2(employees, cooEmployee);
|
|
2810
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2811
|
+
}
|
|
2812
|
+
const cooIdentityContent = getIdentityTemplate("coo");
|
|
2813
|
+
if (cooIdentityContent) {
|
|
2814
|
+
const cooIdPath = identityPath2(cooName);
|
|
2815
|
+
mkdirSync3(path8.dirname(cooIdPath), { recursive: true });
|
|
2816
|
+
const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
|
|
2817
|
+
writeFileSync3(cooIdPath, replaced, "utf-8");
|
|
2747
2818
|
}
|
|
2819
|
+
registerBinSymlinks2(cooName);
|
|
2820
|
+
createdEmployees.push({ name: cooName, role: "COO" });
|
|
2821
|
+
state.completedSteps.push(6);
|
|
2822
|
+
saveSetupState(state);
|
|
2823
|
+
} else {
|
|
2824
|
+
log("Step 6 already complete \u2014 skipping.");
|
|
2825
|
+
const roster = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2826
|
+
const existingCoo = roster.find((e) => e.role === "COO");
|
|
2827
|
+
if (existingCoo) cooName = existingCoo.name;
|
|
2748
2828
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
log(
|
|
2829
|
+
log("");
|
|
2830
|
+
if (!state.completedSteps.includes(7)) {
|
|
2831
|
+
log("=== Meet Your Specialists ===");
|
|
2752
2832
|
log("");
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2833
|
+
log("Your COO coordinates specialists. Here's who you can hire:");
|
|
2834
|
+
log("");
|
|
2835
|
+
log(" CTO (default: yoshi)");
|
|
2836
|
+
log(" Your head of engineering. Architecture, code reviews, tech decisions.");
|
|
2837
|
+
log(" Manages your projects and delegates to engineers when there's parallel work.");
|
|
2838
|
+
log("");
|
|
2839
|
+
log(" CMO (default: mari)");
|
|
2840
|
+
log(" Design, brand, content, SEO. Builds your visual identity, writes");
|
|
2841
|
+
log(" your copy, and gets you found online. Delegates to content specialists.");
|
|
2842
|
+
log("");
|
|
2843
|
+
log("Why separate roles instead of one AI that does everything?");
|
|
2844
|
+
log("");
|
|
2845
|
+
log("Memory saturates. One agent juggling architecture decisions AND landing page");
|
|
2846
|
+
log("copy AND CI/CD AND social media loses context on all of them. Competing");
|
|
2847
|
+
log("priorities \u2014 should I fix the auth bug or write the blog post? When you split");
|
|
2848
|
+
log("responsibilities, each specialist stays sharp because they stay focused.");
|
|
2849
|
+
log("Your COO connects them so nothing falls through the cracks.");
|
|
2850
|
+
log("");
|
|
2851
|
+
log("This is how real companies scale \u2014 you're just doing it with AI");
|
|
2852
|
+
log("instead of headcount.");
|
|
2853
|
+
log("");
|
|
2854
|
+
await ask(rl, "Press Enter to continue. ");
|
|
2855
|
+
state.completedSteps.push(7);
|
|
2856
|
+
saveSetupState(state);
|
|
2857
|
+
} else {
|
|
2858
|
+
log("Step 7 already complete \u2014 skipping.");
|
|
2859
|
+
}
|
|
2860
|
+
if (!state.completedSteps.includes(8)) {
|
|
2861
|
+
let employees = await loadEmployees2(EMPLOYEES_PATH2).catch(() => []);
|
|
2862
|
+
let license;
|
|
2863
|
+
try {
|
|
2864
|
+
license = await checkLicense2();
|
|
2865
|
+
} catch {
|
|
2866
|
+
license = { valid: true, plan: "free", email: "", expiresAt: null, deviceLimit: 1, employeeLimit: 2, memoryLimit: 1e3 };
|
|
2867
|
+
}
|
|
2868
|
+
if (license.plan === "free") {
|
|
2869
|
+
log("Your plan: Free \u2014 1 employee (your COO)");
|
|
2870
|
+
log("");
|
|
2871
|
+
log("The CTO and CMO are available on Solopreneur ($97/mo) \u2014 full engineering");
|
|
2872
|
+
log("and marketing team, unlimited tasks, priority support.");
|
|
2873
|
+
log("Get your key at https://askexe.com, then paste it here.");
|
|
2874
|
+
log("");
|
|
2875
|
+
const licenseInput = await ask(rl, "License key (or press Enter to skip): ");
|
|
2876
|
+
if (licenseInput.startsWith("exe_sk_")) {
|
|
2877
|
+
saveLicense2(licenseInput);
|
|
2878
|
+
mirrorLicenseKey2(licenseInput);
|
|
2879
|
+
try {
|
|
2880
|
+
license = await validateLicense2(licenseInput);
|
|
2881
|
+
} catch {
|
|
2882
|
+
log("Couldn't reach the license server \u2014 your key has been saved.");
|
|
2883
|
+
log("Run exe-os --activate <key> anytime to finish activation.");
|
|
2884
|
+
}
|
|
2885
|
+
} else if (!licenseInput) {
|
|
2886
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
2887
|
+
} else {
|
|
2888
|
+
log("That doesn't look like a license key (should start with exe_sk_).");
|
|
2889
|
+
log("You can activate anytime with: exe-os --activate <key>");
|
|
2775
2890
|
}
|
|
2776
|
-
registerBinSymlinks2(ctoName);
|
|
2777
|
-
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
2778
|
-
log(`Created ${ctoName} (CTO)`);
|
|
2779
2891
|
}
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
const
|
|
2787
|
-
const
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2892
|
+
if (license.plan !== "free") {
|
|
2893
|
+
const planName = license.plan === "pro" ? "Solopreneur" : license.plan.charAt(0).toUpperCase() + license.plan.slice(1);
|
|
2894
|
+
log(`Your plan: ${planName} (up to ${license.employeeLimit} employees)`);
|
|
2895
|
+
log("");
|
|
2896
|
+
const createCto = await ask(rl, "Create your CTO? (Y/n): ");
|
|
2897
|
+
if (createCto.toLowerCase() !== "n") {
|
|
2898
|
+
const ctoTemplate = getTemplateByRole2("CTO");
|
|
2899
|
+
const ctoDefault = ctoTemplate?.name ?? "cto";
|
|
2900
|
+
const ctoNameInput = await ask(rl, `Name your CTO (default: ${ctoDefault}): `);
|
|
2901
|
+
const ctoName = (ctoNameInput || ctoDefault).toLowerCase();
|
|
2902
|
+
if (!employees.some((e) => e.name === ctoName)) {
|
|
2903
|
+
const { personalizePrompt: personalizeCto } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2904
|
+
const ctoEmployee = {
|
|
2905
|
+
name: ctoName,
|
|
2906
|
+
role: "CTO",
|
|
2907
|
+
systemPrompt: personalizeCto(ctoTemplate?.systemPrompt ?? "", ctoDefault, ctoName),
|
|
2908
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2909
|
+
};
|
|
2910
|
+
employees = addEmployee2(employees, ctoEmployee);
|
|
2911
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2912
|
+
}
|
|
2913
|
+
const ctoIdentityContent = getIdentityTemplate("cto");
|
|
2914
|
+
if (ctoIdentityContent) {
|
|
2915
|
+
const ctoIdPath = identityPath2(ctoName);
|
|
2916
|
+
mkdirSync3(path8.dirname(ctoIdPath), { recursive: true });
|
|
2917
|
+
const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
|
|
2918
|
+
writeFileSync3(ctoIdPath, replaced, "utf-8");
|
|
2919
|
+
}
|
|
2920
|
+
registerBinSymlinks2(ctoName);
|
|
2921
|
+
createdEmployees.push({ name: ctoName, role: "CTO" });
|
|
2922
|
+
log(`Created ${ctoName} (CTO)`);
|
|
2795
2923
|
}
|
|
2796
|
-
const
|
|
2797
|
-
if (
|
|
2798
|
-
const
|
|
2799
|
-
|
|
2800
|
-
const
|
|
2801
|
-
|
|
2924
|
+
const createCmo = await ask(rl, "Create your CMO? (Y/n): ");
|
|
2925
|
+
if (createCmo.toLowerCase() !== "n") {
|
|
2926
|
+
const cmoTemplate = getTemplateByRole2("CMO");
|
|
2927
|
+
const cmoDefault = cmoTemplate?.name ?? "cmo";
|
|
2928
|
+
const cmoNameInput = await ask(rl, `Name your CMO (default: ${cmoDefault}): `);
|
|
2929
|
+
const cmoName = (cmoNameInput || cmoDefault).toLowerCase();
|
|
2930
|
+
if (!employees.some((e) => e.name === cmoName)) {
|
|
2931
|
+
const { personalizePrompt: personalizeCmo } = await Promise.resolve().then(() => (init_employee_templates(), employee_templates_exports));
|
|
2932
|
+
const cmoEmployee = {
|
|
2933
|
+
name: cmoName,
|
|
2934
|
+
role: "CMO",
|
|
2935
|
+
systemPrompt: personalizeCmo(cmoTemplate?.systemPrompt ?? "", cmoDefault, cmoName),
|
|
2936
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2937
|
+
};
|
|
2938
|
+
employees = addEmployee2(employees, cmoEmployee);
|
|
2939
|
+
await saveEmployees2(employees, EMPLOYEES_PATH2);
|
|
2940
|
+
}
|
|
2941
|
+
const cmoIdentityContent = getIdentityTemplate("cmo");
|
|
2942
|
+
if (cmoIdentityContent) {
|
|
2943
|
+
const cmoIdPath = identityPath2(cmoName);
|
|
2944
|
+
mkdirSync3(path8.dirname(cmoIdPath), { recursive: true });
|
|
2945
|
+
const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
|
|
2946
|
+
writeFileSync3(cmoIdPath, replaced, "utf-8");
|
|
2947
|
+
}
|
|
2948
|
+
registerBinSymlinks2(cmoName);
|
|
2949
|
+
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
2950
|
+
log(`Created ${cmoName} (CMO)`);
|
|
2802
2951
|
}
|
|
2803
|
-
|
|
2804
|
-
createdEmployees.push({ name: cmoName, role: "CMO" });
|
|
2805
|
-
log(`Created ${cmoName} (CMO)`);
|
|
2952
|
+
log("");
|
|
2806
2953
|
}
|
|
2807
|
-
|
|
2954
|
+
state.completedSteps.push(8);
|
|
2955
|
+
saveSetupState(state);
|
|
2956
|
+
} else {
|
|
2957
|
+
log("Step 8 already complete \u2014 skipping.");
|
|
2808
2958
|
}
|
|
2959
|
+
clearSetupState();
|
|
2809
2960
|
log("=== Two Ways to Work ===");
|
|
2810
2961
|
log("");
|
|
2811
2962
|
log(" 1. Claude Code mode");
|