assistme 0.8.1 → 0.8.3
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/{chunk-QHMIXIWO.js → chunk-3UNXN3BX.js} +129 -44
- package/dist/chunk-HY3FFXSQ.js +113 -0
- package/dist/{chunk-4RHN77BN.js → chunk-UHGTMSLZ.js} +640 -327
- package/dist/config-2HH7PO34.js +20 -0
- package/dist/index.js +643 -670
- package/dist/job-runner-P4DIXXCV.js +7 -0
- package/dist/workers/entry.js +548 -649
- package/package.json +1 -1
- package/src/NAMING.md +34 -0
- package/src/agent/heartbeat-checks.ts +158 -0
- package/src/agent/heartbeat-compiler.ts +122 -0
- package/src/agent/heartbeat-types.ts +62 -0
- package/src/agent/job-analysis-poller.ts +96 -0
- package/src/agent/job-runner.ts +31 -0
- package/src/agent/proactive-monitor.ts +88 -410
- package/src/agent/processor.ts +147 -252
- package/src/agent/prompt-builder.ts +83 -0
- package/src/agent/scheduler.ts +1 -1
- package/src/agent/sdk-stream.ts +105 -0
- package/src/agent/self-analyzer.ts +22 -23
- package/src/agent/session-heartbeat.ts +34 -0
- package/src/agent/skill-db.ts +170 -0
- package/src/agent/skill-evaluator.ts +4 -13
- package/src/agent/skill-format.ts +96 -0
- package/src/agent/skill-marketplace.ts +98 -0
- package/src/agent/skill-search.ts +315 -0
- package/src/agent/skill-types.ts +68 -0
- package/src/agent/skill-utils.ts +66 -0
- package/src/agent/skills.ts +265 -566
- package/src/agent/system-prompt.ts +11 -8
- package/src/agent/task-poller.ts +101 -0
- package/src/agent/task-timeout.ts +50 -0
- package/src/browser/chrome-launcher.ts +6 -6
- package/src/browser/controller.ts +1 -2
- package/src/commands/auth.ts +4 -11
- package/src/commands/browser.ts +12 -47
- package/src/commands/credential.ts +2 -2
- package/src/commands/job.ts +3 -3
- package/src/commands/monitor.ts +3 -5
- package/src/commands/start.ts +3 -3
- package/src/credentials/encryption.ts +4 -10
- package/src/credentials/local-store.ts +4 -7
- package/src/credentials/program-store.ts +8 -10
- package/src/db/analysis-data.ts +4 -1
- package/src/db/auth-store.ts +15 -13
- package/src/db/session-log.ts +13 -5
- package/src/mcp/agent-tools-server.ts +126 -102
- package/src/mcp/ask-user.ts +102 -0
- package/src/mcp/skill-confirmation.ts +109 -0
- package/src/orchestrator.ts +100 -249
- package/src/tools/filesystem.ts +48 -2
- package/src/tools/index.ts +1 -2
- package/src/tools/shell.ts +58 -2
- package/src/types/edsger-feedback.d.ts +18 -0
- package/src/utils/config.ts +79 -1
- package/src/utils/logger.ts +100 -10
- package/src/utils/lru-cache.ts +57 -0
- package/src/utils/schemas.ts +2 -0
- package/src/workers/base-handler.ts +18 -1
- package/src/workers/conversation.ts +4 -1
- package/src/workers/entry.ts +1 -5
- package/src/workers/index.ts +2 -0
- package/src/workers/log-forwarder.ts +55 -0
- package/src/workers/manager.ts +85 -209
- package/src/workers/types.ts +21 -5
- package/src/workers/worker-lifecycle.ts +113 -0
- package/tests/agent/heartbeat-checks.test.ts +160 -0
- package/tests/agent/mcp-servers.test.ts +1 -1
- package/tests/agent/proactive-monitor.test.ts +42 -26
- package/tests/agent/processor.test.ts +2 -1
- package/tests/agent/sdk-stream.test.ts +181 -0
- package/tests/agent/self-analyzer.test.ts +106 -49
- package/tests/agent/session.test.ts +114 -132
- package/tests/agent/skill-format.test.ts +93 -0
- package/tests/agent/skill-search.test.ts +175 -0
- package/tests/agent/skill-utils.test.ts +86 -0
- package/tests/agent/skills.test.ts +4 -24
- package/tests/db/supabase.test.ts +3 -2
- package/tests/mcp/ask-user.test.ts +117 -0
- package/tests/mcp/skill-confirmation.integration.test.ts +216 -0
- package/tests/mcp/skill-confirmation.test.ts +66 -0
- package/tests/tools/filesystem.test.ts +38 -1
- package/tests/tools/shell.test.ts +43 -0
- package/tests/utils/config.test.ts +2 -2
- package/dist/chunk-YYSJHZSO.js +0 -47
- package/dist/config-3RWSAUAZ.js +0 -12
- package/dist/job-runner-YM2NBIL3.js +0 -7
- package/src/agent/session.ts +0 -348
- package/src/workers/job-analyzer.ts +0 -415
|
@@ -1,32 +1,33 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
errorMessage,
|
|
3
|
+
getConfig,
|
|
4
|
+
getDataDir
|
|
5
|
+
} from "./chunk-HY3FFXSQ.js";
|
|
4
6
|
|
|
5
7
|
// src/db/auth-store.ts
|
|
6
|
-
import { existsSync,
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
9
|
import { join } from "path";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var AUTH_FILE = join(AUTH_DIR, "auth.json");
|
|
11
|
-
function ensureAuthDir() {
|
|
12
|
-
if (!existsSync(AUTH_DIR)) {
|
|
13
|
-
mkdirSync(AUTH_DIR, { recursive: true, mode: 448 });
|
|
14
|
-
}
|
|
10
|
+
function getAuthFile() {
|
|
11
|
+
return join(getDataDir(), "auth.json");
|
|
15
12
|
}
|
|
16
13
|
function readAuthStore() {
|
|
17
14
|
try {
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
const authFile = getAuthFile();
|
|
16
|
+
if (existsSync(authFile)) {
|
|
17
|
+
return JSON.parse(readFileSync(authFile, "utf-8"));
|
|
20
18
|
}
|
|
21
19
|
} catch {
|
|
22
20
|
}
|
|
23
21
|
return {};
|
|
24
22
|
}
|
|
25
23
|
function writeAuthStore(data) {
|
|
26
|
-
|
|
27
|
-
writeFileSync(AUTH_FILE, JSON.stringify(data, null, 2), { mode: 384 });
|
|
24
|
+
writeFileSync(getAuthFile(), JSON.stringify(data, null, 2), { mode: 384 });
|
|
28
25
|
}
|
|
29
26
|
function getRawToken() {
|
|
27
|
+
const envToken = process.env.ASSISTME_TOKEN;
|
|
28
|
+
if (envToken && envToken.startsWith("am_")) {
|
|
29
|
+
return envToken;
|
|
30
|
+
}
|
|
30
31
|
const store = readAuthStore();
|
|
31
32
|
const token = store["mcp_token"];
|
|
32
33
|
if (!token || !token.startsWith("am_")) {
|
|
@@ -59,9 +60,28 @@ async function callMcpHandler(action, params = {}, overrideToken) {
|
|
|
59
60
|
// src/utils/logger.ts
|
|
60
61
|
import chalk from "chalk";
|
|
61
62
|
import { randomUUID } from "crypto";
|
|
63
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
64
|
+
import { join as join2, dirname } from "path";
|
|
65
|
+
import { fileURLToPath } from "url";
|
|
62
66
|
var currentLevel = "info";
|
|
63
67
|
var currentCorrelationId = null;
|
|
68
|
+
var currentConversationId = null;
|
|
64
69
|
var logHook = null;
|
|
70
|
+
var logTransport = null;
|
|
71
|
+
var PKG_VERSION = (() => {
|
|
72
|
+
try {
|
|
73
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
74
|
+
for (const rel of ["../package.json", "../../package.json"]) {
|
|
75
|
+
try {
|
|
76
|
+
const pkg = JSON.parse(readFileSync2(join2(__dirname, rel), "utf-8"));
|
|
77
|
+
if (pkg.version) return pkg.version;
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
return "unknown";
|
|
84
|
+
})();
|
|
65
85
|
var LEVEL_ORDER = {
|
|
66
86
|
debug: 0,
|
|
67
87
|
info: 1,
|
|
@@ -74,9 +94,15 @@ function setLogLevel(level) {
|
|
|
74
94
|
function setLogHook(hook) {
|
|
75
95
|
logHook = hook;
|
|
76
96
|
}
|
|
97
|
+
function setLogTransport(transport) {
|
|
98
|
+
logTransport = transport;
|
|
99
|
+
}
|
|
77
100
|
function setCorrelationId(id) {
|
|
78
101
|
currentCorrelationId = id;
|
|
79
102
|
}
|
|
103
|
+
function setLogConversationId(id) {
|
|
104
|
+
currentConversationId = id;
|
|
105
|
+
}
|
|
80
106
|
function newCorrelationId() {
|
|
81
107
|
const id = randomUUID().slice(0, 8);
|
|
82
108
|
currentCorrelationId = id;
|
|
@@ -90,54 +116,87 @@ function timestamp() {
|
|
|
90
116
|
}
|
|
91
117
|
function prefix() {
|
|
92
118
|
const ts = timestamp();
|
|
93
|
-
|
|
119
|
+
const base = `${ts} v${PKG_VERSION}`;
|
|
120
|
+
return currentCorrelationId ? `${base} ${currentCorrelationId}` : base;
|
|
94
121
|
}
|
|
95
122
|
var log = {
|
|
96
123
|
debug(msg, ...args) {
|
|
97
124
|
if (shouldLog("debug")) {
|
|
98
125
|
const text = formatArgs(msg, args);
|
|
99
|
-
|
|
126
|
+
if (logTransport) {
|
|
127
|
+
logTransport("debug", text);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
logHook?.("stdout", `[DEBUG] ${text}`, currentConversationId);
|
|
100
131
|
console.log(chalk.gray(`[${prefix()}] DEBUG`), msg, ...args);
|
|
101
132
|
}
|
|
102
133
|
},
|
|
103
134
|
info(msg, ...args) {
|
|
104
135
|
if (shouldLog("info")) {
|
|
105
136
|
const text = formatArgs(msg, args);
|
|
106
|
-
|
|
137
|
+
if (logTransport) {
|
|
138
|
+
logTransport("info", text);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
logHook?.("stdout", text, currentConversationId);
|
|
107
142
|
console.log(chalk.blue(`[${prefix()}]`), msg, ...args);
|
|
108
143
|
}
|
|
109
144
|
},
|
|
110
145
|
success(msg, ...args) {
|
|
111
146
|
if (shouldLog("info")) {
|
|
112
147
|
const text = formatArgs(msg, args);
|
|
113
|
-
|
|
148
|
+
if (logTransport) {
|
|
149
|
+
logTransport("success", text);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
logHook?.("stdout", `\u2713 ${text}`, currentConversationId);
|
|
114
153
|
console.log(chalk.green(`[${prefix()}] \u2713`), msg, ...args);
|
|
115
154
|
}
|
|
116
155
|
},
|
|
117
156
|
warn(msg, ...args) {
|
|
118
157
|
if (shouldLog("warn")) {
|
|
119
158
|
const text = formatArgs(msg, args);
|
|
120
|
-
|
|
159
|
+
if (logTransport) {
|
|
160
|
+
logTransport("warn", text);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
logHook?.("stderr", `[WARN] ${text}`, currentConversationId);
|
|
121
164
|
console.log(chalk.yellow(`[${prefix()}] WARN`), msg, ...args);
|
|
122
165
|
}
|
|
123
166
|
},
|
|
124
167
|
error(msg, ...args) {
|
|
125
168
|
if (shouldLog("error")) {
|
|
126
169
|
const text = formatArgs(msg, args);
|
|
127
|
-
|
|
170
|
+
if (logTransport) {
|
|
171
|
+
logTransport("error", text);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
logHook?.("stderr", `[ERROR] ${text}`, currentConversationId);
|
|
128
175
|
console.error(chalk.red(`[${prefix()}] ERROR`), msg, ...args);
|
|
129
176
|
}
|
|
130
177
|
},
|
|
131
178
|
agent(msg) {
|
|
132
|
-
|
|
179
|
+
if (logTransport) {
|
|
180
|
+
logTransport("agent", msg);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
logHook?.("stdout", `\u25B8 ${msg}`, currentConversationId);
|
|
133
184
|
console.log(chalk.cyan(" \u25B8"), msg);
|
|
134
185
|
},
|
|
135
186
|
tool(name, msg) {
|
|
136
|
-
|
|
187
|
+
if (logTransport) {
|
|
188
|
+
logTransport("tool", msg, name);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
logHook?.("stdout", `\u26A1 ${name}: ${msg}`, currentConversationId);
|
|
137
192
|
console.log(chalk.magenta(` \u26A1 ${name}:`), msg);
|
|
138
193
|
},
|
|
139
194
|
result(msg) {
|
|
140
|
-
|
|
195
|
+
if (logTransport) {
|
|
196
|
+
logTransport("result", msg);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
logHook?.("stdout", `\u2190 ${msg}`, currentConversationId);
|
|
141
200
|
console.log(chalk.green(" \u2190"), msg);
|
|
142
201
|
}
|
|
143
202
|
};
|
|
@@ -294,25 +353,6 @@ var HEARTBEAT_MAX_BUDGET_USD = 0.25;
|
|
|
294
353
|
var HEARTBEAT_LOG_MAX_ENTRIES = 100;
|
|
295
354
|
var MAX_COMPLETE_TASK_RETRIES = 2;
|
|
296
355
|
|
|
297
|
-
// src/utils/errors.ts
|
|
298
|
-
var AppError = class _AppError extends Error {
|
|
299
|
-
constructor(message, code, cause) {
|
|
300
|
-
super(message);
|
|
301
|
-
this.code = code;
|
|
302
|
-
this.cause = cause;
|
|
303
|
-
this.name = "AppError";
|
|
304
|
-
}
|
|
305
|
-
static fromUnknown(err, code = "INTERNAL") {
|
|
306
|
-
if (err instanceof _AppError) return err;
|
|
307
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
308
|
-
return new _AppError(message, code, err);
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
function errorMessage(err) {
|
|
312
|
-
if (err instanceof Error) return err.message;
|
|
313
|
-
return String(err);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
356
|
// src/agent/job-runner.ts
|
|
317
357
|
var JobRunner = class {
|
|
318
358
|
/**
|
|
@@ -417,6 +457,51 @@ var JobRunner = class {
|
|
|
417
457
|
return [];
|
|
418
458
|
}
|
|
419
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Build a prompt for analyzing a job's skill requirements.
|
|
462
|
+
* Used by the orchestrator to dispatch analysis tasks for newly created jobs.
|
|
463
|
+
*/
|
|
464
|
+
buildJobAnalysisPrompt(job) {
|
|
465
|
+
let prompt = `Analyze this job definition and determine what skills are needed to execute it effectively.
|
|
466
|
+
|
|
467
|
+
`;
|
|
468
|
+
prompt += `## Job Definition
|
|
469
|
+
`;
|
|
470
|
+
prompt += `**Name:** ${job.jobName}
|
|
471
|
+
`;
|
|
472
|
+
prompt += `**Description:** ${job.jobDescription}
|
|
473
|
+
|
|
474
|
+
`;
|
|
475
|
+
if (job.skills.length > 0) {
|
|
476
|
+
prompt += `**Current Skills:**
|
|
477
|
+
`;
|
|
478
|
+
for (const skill of job.skills) {
|
|
479
|
+
const emoji = skill.skillEmoji ? `${skill.skillEmoji} ` : "";
|
|
480
|
+
prompt += `- ${emoji}${skill.skillName}: ${skill.skillDescription}
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
prompt += `
|
|
484
|
+
`;
|
|
485
|
+
}
|
|
486
|
+
prompt += `## Instructions
|
|
487
|
+
`;
|
|
488
|
+
prompt += `For each capability the job needs:
|
|
489
|
+
`;
|
|
490
|
+
prompt += `1. Check if an existing skill covers it (use \`skill_search\` to check)
|
|
491
|
+
`;
|
|
492
|
+
prompt += `2. If no existing skill covers it, create a new one using \`skill_create\`
|
|
493
|
+
`;
|
|
494
|
+
prompt += `3. If an existing skill needs improvement, update it using \`skill_improve\`
|
|
495
|
+
`;
|
|
496
|
+
prompt += `4. Link all relevant skills to the job using \`skill_link_job\`
|
|
497
|
+
|
|
498
|
+
`;
|
|
499
|
+
prompt += `Be practical \u2014 only create skills that would genuinely help automate this job.
|
|
500
|
+
`;
|
|
501
|
+
prompt += `When done, summarize what skills were created, updated, or already existed.
|
|
502
|
+
`;
|
|
503
|
+
return prompt;
|
|
504
|
+
}
|
|
420
505
|
/**
|
|
421
506
|
* Build the agentic prompt for the agent to execute a job.
|
|
422
507
|
*
|
|
@@ -480,7 +565,9 @@ export {
|
|
|
480
565
|
callMcpHandler,
|
|
481
566
|
setLogLevel,
|
|
482
567
|
setLogHook,
|
|
568
|
+
setLogTransport,
|
|
483
569
|
setCorrelationId,
|
|
570
|
+
setLogConversationId,
|
|
484
571
|
newCorrelationId,
|
|
485
572
|
log,
|
|
486
573
|
MAX_RESPONSE_CONTENT_LENGTH,
|
|
@@ -514,8 +601,6 @@ export {
|
|
|
514
601
|
HEARTBEAT_MAX_BUDGET_USD,
|
|
515
602
|
HEARTBEAT_LOG_MAX_ENTRIES,
|
|
516
603
|
MAX_COMPLETE_TASK_RETRIES,
|
|
517
|
-
AppError,
|
|
518
|
-
errorMessage,
|
|
519
604
|
safeParse,
|
|
520
605
|
SkillRowSchema,
|
|
521
606
|
SkillCreateResultSchema,
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// src/utils/config.ts
|
|
2
|
+
import Conf from "conf";
|
|
3
|
+
import { resolve, basename, join, relative, sep } from "path";
|
|
4
|
+
import { existsSync, mkdirSync } from "fs";
|
|
5
|
+
|
|
6
|
+
// src/utils/errors.ts
|
|
7
|
+
var AppError = class _AppError extends Error {
|
|
8
|
+
constructor(message, code, cause) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.cause = cause;
|
|
12
|
+
this.name = "AppError";
|
|
13
|
+
}
|
|
14
|
+
static fromUnknown(err, code = "INTERNAL") {
|
|
15
|
+
if (err instanceof _AppError) return err;
|
|
16
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17
|
+
return new _AppError(message, code, err);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function errorMessage(err) {
|
|
21
|
+
if (err instanceof Error) return err.message;
|
|
22
|
+
return String(err);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/utils/config.ts
|
|
26
|
+
var SUPABASE_URL_DEFAULT = process.env.ASSISTME_SUPABASE_URL || "https://msgplwbgohpokajtibew.supabase.co";
|
|
27
|
+
var SUPABASE_ANON_KEY_DEFAULT = process.env.ASSISTME_SUPABASE_ANON_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im1zZ3Bsd2Jnb2hwb2thanRpYmV3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjE3MTMzNTEsImV4cCI6MjA3NzI4OTM1MX0.YqiluL_mIBWKv5dteIcPAPQ_jRp9rzZlvAXvkQttDjs";
|
|
28
|
+
var CONFIG_DEFAULTS = {
|
|
29
|
+
supabaseUrl: SUPABASE_URL_DEFAULT,
|
|
30
|
+
supabaseAnonKey: SUPABASE_ANON_KEY_DEFAULT,
|
|
31
|
+
sessionName: "Default",
|
|
32
|
+
model: "claude-sonnet-4-20250514",
|
|
33
|
+
taskTimeoutMinutes: 10
|
|
34
|
+
};
|
|
35
|
+
var config = new Conf({
|
|
36
|
+
projectName: "assistme",
|
|
37
|
+
defaults: CONFIG_DEFAULTS
|
|
38
|
+
});
|
|
39
|
+
function getConfig() {
|
|
40
|
+
const supabaseUrl = process.env.SUPABASE_URL || config.get("supabaseUrl") || SUPABASE_URL_DEFAULT;
|
|
41
|
+
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || config.get("supabaseAnonKey") || SUPABASE_ANON_KEY_DEFAULT;
|
|
42
|
+
const anthropicApiKey = process.env.ANTHROPIC_API_KEY || config.get("anthropicApiKey") || "";
|
|
43
|
+
const workspacePath = config.get("workspacePath") || process.cwd();
|
|
44
|
+
return {
|
|
45
|
+
supabaseUrl,
|
|
46
|
+
supabaseAnonKey,
|
|
47
|
+
anthropicApiKey,
|
|
48
|
+
workspacePath: resolve(workspacePath),
|
|
49
|
+
sessionName: config.get("sessionName") || "Default",
|
|
50
|
+
model: config.get("model") || "claude-sonnet-4-20250514",
|
|
51
|
+
taskTimeoutMinutes: config.get("taskTimeoutMinutes") || 10
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function setConfig(key, value) {
|
|
55
|
+
config.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
function clearConfig() {
|
|
58
|
+
config.clear();
|
|
59
|
+
}
|
|
60
|
+
function getConfigPath() {
|
|
61
|
+
return config.path;
|
|
62
|
+
}
|
|
63
|
+
var DATA_DIR_NAME = ".assistme-data";
|
|
64
|
+
var _rootCache = null;
|
|
65
|
+
var _dataDirCache = null;
|
|
66
|
+
function getAssistMeRoot() {
|
|
67
|
+
if (_rootCache) return _rootCache;
|
|
68
|
+
const { workspacePath } = getConfig();
|
|
69
|
+
const base = basename(workspacePath);
|
|
70
|
+
_rootCache = base === "assistme" ? workspacePath : join(workspacePath, "assistme");
|
|
71
|
+
if (!existsSync(_rootCache)) {
|
|
72
|
+
mkdirSync(_rootCache, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
return _rootCache;
|
|
75
|
+
}
|
|
76
|
+
function getDataDir() {
|
|
77
|
+
if (_dataDirCache) return _dataDirCache;
|
|
78
|
+
const dataDir = join(getAssistMeRoot(), DATA_DIR_NAME);
|
|
79
|
+
if (!existsSync(dataDir)) {
|
|
80
|
+
mkdirSync(dataDir, { recursive: true, mode: 448 });
|
|
81
|
+
}
|
|
82
|
+
_dataDirCache = dataDir;
|
|
83
|
+
return dataDir;
|
|
84
|
+
}
|
|
85
|
+
function assertWithinAssistMeRoot(filePath) {
|
|
86
|
+
const root = getAssistMeRoot();
|
|
87
|
+
const resolved = resolve(root, filePath);
|
|
88
|
+
const rel = relative(root, resolved);
|
|
89
|
+
if (rel.startsWith("..") || rel.startsWith(sep + sep)) {
|
|
90
|
+
throw new AppError(
|
|
91
|
+
`Access denied: path "${filePath}" is outside assistme workspace "${root}"`,
|
|
92
|
+
"PATH_TRAVERSAL"
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
return resolved;
|
|
96
|
+
}
|
|
97
|
+
function resetDirCaches() {
|
|
98
|
+
_rootCache = null;
|
|
99
|
+
_dataDirCache = null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export {
|
|
103
|
+
AppError,
|
|
104
|
+
errorMessage,
|
|
105
|
+
getConfig,
|
|
106
|
+
setConfig,
|
|
107
|
+
clearConfig,
|
|
108
|
+
getConfigPath,
|
|
109
|
+
getAssistMeRoot,
|
|
110
|
+
getDataDir,
|
|
111
|
+
assertWithinAssistMeRoot,
|
|
112
|
+
resetDirCaches
|
|
113
|
+
};
|