eminiar 0.8.0 → 0.8.1
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/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -1
- package/dist/hook.js +147 -48
- package/dist/server.js +34 -8
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eminiar",
|
|
3
3
|
"displayName": "Eminiar",
|
|
4
|
-
"version": "0.8.
|
|
4
|
+
"version": "0.8.1",
|
|
5
5
|
"description": "Local-first context, memory and knowledge graph for Claude Code: sandboxed execution, FTS5 + vector search, session continuity, code graph.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ryan Dick"
|
package/README.md
CHANGED
|
@@ -58,7 +58,8 @@ Everything runs locally in one SQLite database per project.
|
|
|
58
58
|
`em_execute`, `em_batch_execute` and `em_execute_file` run shell, JavaScript and TypeScript, plus
|
|
59
59
|
Python, Ruby, Go, PHP, Perl, R, Elixir, Rust and C# when they are installed. Output over 5 KB is
|
|
60
60
|
indexed. Claude gets back only the sections that match its `intent`. Snippets obey your Claude
|
|
61
|
-
Code `
|
|
61
|
+
Code `Bash` deny rules, and `em_execute_file` refuses a `path` that one of your `Read` deny rules
|
|
62
|
+
matches ([which rule forms](docs/how-it-works.md#sandbox)).
|
|
62
63
|
|
|
63
64
|
### 🔎 Hybrid search — keyword and meaning together
|
|
64
65
|
|
package/dist/hook.js
CHANGED
|
@@ -3941,11 +3941,11 @@ var require_out = __commonJS({
|
|
|
3941
3941
|
async.read(path2, getSettings(optionsOrSettingsOrCallback), callback);
|
|
3942
3942
|
}
|
|
3943
3943
|
exports.stat = stat;
|
|
3944
|
-
function
|
|
3944
|
+
function statSync6(path2, optionsOrSettings) {
|
|
3945
3945
|
const settings = getSettings(optionsOrSettings);
|
|
3946
3946
|
return sync.read(path2, settings);
|
|
3947
3947
|
}
|
|
3948
|
-
exports.statSync =
|
|
3948
|
+
exports.statSync = statSync6;
|
|
3949
3949
|
function getSettings(settingsOrOptions = {}) {
|
|
3950
3950
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
3951
3951
|
return settingsOrOptions;
|
|
@@ -4403,7 +4403,7 @@ var require_queue = __commonJS({
|
|
|
4403
4403
|
if (!(_concurrency >= 1)) {
|
|
4404
4404
|
throw new Error("fastqueue concurrency must be equal to or greater than 1");
|
|
4405
4405
|
}
|
|
4406
|
-
var
|
|
4406
|
+
var cache3 = reusify(Task);
|
|
4407
4407
|
var queueHead = null;
|
|
4408
4408
|
var queueTail = null;
|
|
4409
4409
|
var _running = 0;
|
|
@@ -4482,7 +4482,7 @@ var require_queue = __commonJS({
|
|
|
4482
4482
|
return _running === 0 && self.length() === 0;
|
|
4483
4483
|
}
|
|
4484
4484
|
function push(value, done) {
|
|
4485
|
-
var current =
|
|
4485
|
+
var current = cache3.get();
|
|
4486
4486
|
current.context = context;
|
|
4487
4487
|
current.release = release;
|
|
4488
4488
|
current.value = value;
|
|
@@ -4503,7 +4503,7 @@ var require_queue = __commonJS({
|
|
|
4503
4503
|
}
|
|
4504
4504
|
}
|
|
4505
4505
|
function unshift(value, done) {
|
|
4506
|
-
var current =
|
|
4506
|
+
var current = cache3.get();
|
|
4507
4507
|
current.context = context;
|
|
4508
4508
|
current.release = release;
|
|
4509
4509
|
current.value = value;
|
|
@@ -4525,7 +4525,7 @@ var require_queue = __commonJS({
|
|
|
4525
4525
|
}
|
|
4526
4526
|
function release(holder) {
|
|
4527
4527
|
if (holder) {
|
|
4528
|
-
|
|
4528
|
+
cache3.release(holder);
|
|
4529
4529
|
}
|
|
4530
4530
|
var next = queueHead;
|
|
4531
4531
|
if (next && _running <= _concurrency) {
|
|
@@ -4575,7 +4575,7 @@ var require_queue = __commonJS({
|
|
|
4575
4575
|
errorHandler2(new Error("abort"), val);
|
|
4576
4576
|
}
|
|
4577
4577
|
callback.call(context2, new Error("abort"));
|
|
4578
|
-
|
|
4578
|
+
cache3.release(current);
|
|
4579
4579
|
current = next;
|
|
4580
4580
|
}
|
|
4581
4581
|
self.drain = noop;
|
|
@@ -25733,21 +25733,21 @@ function visit(schema, fnOrHandlers) {
|
|
|
25733
25733
|
const h = fnOrHandlers[node2._zod.def.type];
|
|
25734
25734
|
return h ? h(node2, rewritten) : node2;
|
|
25735
25735
|
};
|
|
25736
|
-
const
|
|
25736
|
+
const cache3 = /* @__PURE__ */ new Map();
|
|
25737
25737
|
function run(s) {
|
|
25738
|
-
const cached3 =
|
|
25738
|
+
const cached3 = cache3.get(s);
|
|
25739
25739
|
if (cached3 === RESOLVING) {
|
|
25740
25740
|
return new $ZodLazy({
|
|
25741
25741
|
type: "lazy",
|
|
25742
|
-
getter: () =>
|
|
25742
|
+
getter: () => cache3.get(s)
|
|
25743
25743
|
});
|
|
25744
25744
|
}
|
|
25745
25745
|
if (cached3 !== void 0)
|
|
25746
25746
|
return cached3;
|
|
25747
|
-
|
|
25747
|
+
cache3.set(s, RESOLVING);
|
|
25748
25748
|
const inner = mapInner(s);
|
|
25749
25749
|
const mapped = fn(inner, inner !== s);
|
|
25750
|
-
|
|
25750
|
+
cache3.set(s, mapped);
|
|
25751
25751
|
return mapped;
|
|
25752
25752
|
}
|
|
25753
25753
|
function mapInner(s) {
|
|
@@ -28947,8 +28947,8 @@ var preCompact = (input2) => {
|
|
|
28947
28947
|
|
|
28948
28948
|
// src/hooks/routing.ts
|
|
28949
28949
|
init_esm_shims();
|
|
28950
|
-
import { realpathSync as
|
|
28951
|
-
import { extname as extname2, join as
|
|
28950
|
+
import { realpathSync as realpathSync4, statSync as statSync4 } from "fs";
|
|
28951
|
+
import { extname as extname2, join as join11, relative as relative4, resolve as resolve3 } from "path";
|
|
28952
28952
|
|
|
28953
28953
|
// src/docs/manifest.ts
|
|
28954
28954
|
init_esm_shims();
|
|
@@ -28998,10 +28998,107 @@ function readManifest(ctx) {
|
|
|
28998
28998
|
return readManifestAt(manifestPath(ctx));
|
|
28999
28999
|
}
|
|
29000
29000
|
|
|
29001
|
+
// src/permissions/settings.ts
|
|
29002
|
+
init_esm_shims();
|
|
29003
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, realpathSync as realpathSync3, statSync as statSync2 } from "fs";
|
|
29004
|
+
import { homedir as homedir2 } from "os";
|
|
29005
|
+
import { join as join5 } from "path";
|
|
29006
|
+
|
|
29007
|
+
// src/permissions/rules.ts
|
|
29008
|
+
init_esm_shims();
|
|
29009
|
+
function parseRule(raw) {
|
|
29010
|
+
const m = /^([A-Za-z][\w-]*)(?:\((.*)\))?$/s.exec(raw.trim());
|
|
29011
|
+
if (!m) return void 0;
|
|
29012
|
+
return { raw, tool: m[1], pattern: m[2] };
|
|
29013
|
+
}
|
|
29014
|
+
function escapeRe(s) {
|
|
29015
|
+
return s.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
29016
|
+
}
|
|
29017
|
+
var readRegexCache = /* @__PURE__ */ new Map();
|
|
29018
|
+
function readRegex(pattern) {
|
|
29019
|
+
let re = readRegexCache.get(pattern);
|
|
29020
|
+
if (!re) {
|
|
29021
|
+
if (readRegexCache.size >= 1024) readRegexCache.clear();
|
|
29022
|
+
re = new RegExp(
|
|
29023
|
+
`^${pattern.split("**").map((seg) => seg.split("*").map(escapeRe).join("[^/]*")).join(".*")}$`
|
|
29024
|
+
);
|
|
29025
|
+
readRegexCache.set(pattern, re);
|
|
29026
|
+
}
|
|
29027
|
+
return re;
|
|
29028
|
+
}
|
|
29029
|
+
function matchRead(rule, absPath) {
|
|
29030
|
+
if (rule.tool !== "Read") return false;
|
|
29031
|
+
if (rule.pattern === void 0) return true;
|
|
29032
|
+
let p = rule.pattern;
|
|
29033
|
+
if (p.startsWith("~")) p = (process.env.HOME ?? "") + p.slice(1);
|
|
29034
|
+
return readRegex(p).test(absPath);
|
|
29035
|
+
}
|
|
29036
|
+
|
|
29037
|
+
// src/permissions/split.ts
|
|
29038
|
+
init_esm_shims();
|
|
29039
|
+
|
|
29040
|
+
// src/permissions/settings.ts
|
|
29041
|
+
function readRules(file3) {
|
|
29042
|
+
if (!existsSync4(file3)) return {};
|
|
29043
|
+
try {
|
|
29044
|
+
const s = JSON.parse(readFileSync4(file3, "utf8"));
|
|
29045
|
+
return s.permissions ?? {};
|
|
29046
|
+
} catch {
|
|
29047
|
+
return {};
|
|
29048
|
+
}
|
|
29049
|
+
}
|
|
29050
|
+
function stamp(file3) {
|
|
29051
|
+
try {
|
|
29052
|
+
return statSync2(file3).mtimeMs;
|
|
29053
|
+
} catch {
|
|
29054
|
+
return -1;
|
|
29055
|
+
}
|
|
29056
|
+
}
|
|
29057
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
29058
|
+
function loadPermissionRules(root, extraDeny = []) {
|
|
29059
|
+
const files = [
|
|
29060
|
+
join5(homedir2(), ".claude", "settings.json"),
|
|
29061
|
+
join5(root, ".claude", "settings.json"),
|
|
29062
|
+
join5(root, ".claude", "settings.local.json")
|
|
29063
|
+
];
|
|
29064
|
+
const key = `${root}\0${extraDeny.join("\0")}`;
|
|
29065
|
+
const stamps = files.map(stamp);
|
|
29066
|
+
const hit = cache2.get(key);
|
|
29067
|
+
if (hit?.stamps.every((s, i) => s === stamps[i])) return hit.rules;
|
|
29068
|
+
const out = { allow: [], deny: [], ask: [] };
|
|
29069
|
+
const push = (list, raws) => {
|
|
29070
|
+
for (const raw of raws ?? []) {
|
|
29071
|
+
const r = parseRule(raw);
|
|
29072
|
+
if (r) list.push(r);
|
|
29073
|
+
}
|
|
29074
|
+
};
|
|
29075
|
+
for (const f of files) {
|
|
29076
|
+
const p = readRules(f);
|
|
29077
|
+
push(out.allow, p.allow);
|
|
29078
|
+
push(out.deny, p.deny);
|
|
29079
|
+
push(out.ask, p.ask);
|
|
29080
|
+
}
|
|
29081
|
+
push(out.deny, extraDeny);
|
|
29082
|
+
if (cache2.size >= 16) cache2.clear();
|
|
29083
|
+
cache2.set(key, { stamps, rules: out });
|
|
29084
|
+
return out;
|
|
29085
|
+
}
|
|
29086
|
+
function checkRead(rules, absPaths) {
|
|
29087
|
+
for (const kind of ["deny", "ask"]) {
|
|
29088
|
+
for (const r of rules[kind]) {
|
|
29089
|
+
if (absPaths.some((p) => matchRead(r, p))) return { allowed: false, rule: r.raw, kind };
|
|
29090
|
+
}
|
|
29091
|
+
}
|
|
29092
|
+
return { allowed: true };
|
|
29093
|
+
}
|
|
29094
|
+
function checkReadPath(rules, abs) {
|
|
29095
|
+
return checkRead(rules, [.../* @__PURE__ */ new Set([abs, realpathSync3(abs), realpathSync3.native(abs)])]);
|
|
29096
|
+
}
|
|
29097
|
+
|
|
29001
29098
|
// src/session/brief.ts
|
|
29002
29099
|
init_esm_shims();
|
|
29003
|
-
import { existsSync as
|
|
29004
|
-
import { join as
|
|
29100
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
29101
|
+
import { join as join7 } from "path";
|
|
29005
29102
|
|
|
29006
29103
|
// src/memory/frontmatter.ts
|
|
29007
29104
|
init_esm_shims();
|
|
@@ -29035,15 +29132,15 @@ function parseMemoryFile(text) {
|
|
|
29035
29132
|
init_esm_shims();
|
|
29036
29133
|
var import_fast_glob2 = __toESM(require_out4(), 1);
|
|
29037
29134
|
import {
|
|
29038
|
-
existsSync as
|
|
29135
|
+
existsSync as existsSync5,
|
|
29039
29136
|
mkdirSync as mkdirSync3,
|
|
29040
|
-
readFileSync as
|
|
29137
|
+
readFileSync as readFileSync5,
|
|
29041
29138
|
renameSync,
|
|
29042
|
-
statSync as
|
|
29139
|
+
statSync as statSync3,
|
|
29043
29140
|
unlinkSync,
|
|
29044
29141
|
writeFileSync as writeFileSync2
|
|
29045
29142
|
} from "fs";
|
|
29046
|
-
import { dirname as dirname3, join as
|
|
29143
|
+
import { dirname as dirname3, join as join6, relative as relative3, sep } from "path";
|
|
29047
29144
|
|
|
29048
29145
|
// src/storage/types.ts
|
|
29049
29146
|
init_esm_shims();
|
|
@@ -29055,7 +29152,7 @@ function memoriesRel(ctx) {
|
|
|
29055
29152
|
return ctx.config.memories.dir.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
29056
29153
|
}
|
|
29057
29154
|
function memoriesDir(ctx) {
|
|
29058
|
-
return
|
|
29155
|
+
return join6(ctx.root, memoriesRel(ctx));
|
|
29059
29156
|
}
|
|
29060
29157
|
|
|
29061
29158
|
// src/session/brief.ts
|
|
@@ -29098,10 +29195,10 @@ function historyLine(db) {
|
|
|
29098
29195
|
return `${parts.join("; ")}. em_history {action:"file", path} before editing, {action:"risk"} before committing.`;
|
|
29099
29196
|
}
|
|
29100
29197
|
function overviewParagraph(ctx) {
|
|
29101
|
-
const file3 =
|
|
29102
|
-
if (!
|
|
29198
|
+
const file3 = join7(memoriesDir(ctx), "onboarding", "overview.md");
|
|
29199
|
+
if (!existsSync6(file3)) return void 0;
|
|
29103
29200
|
try {
|
|
29104
|
-
const body = parseMemoryFile(
|
|
29201
|
+
const body = parseMemoryFile(readFileSync6(file3, "utf8")).body.trim();
|
|
29105
29202
|
const para = body.split(/\n\s*\n/).map(
|
|
29106
29203
|
(p) => p.replace(/^#+\s.*$/m, "").replace(/\s+/g, " ").trim()
|
|
29107
29204
|
).find((p) => p.length > 20);
|
|
@@ -29150,10 +29247,10 @@ function fitBudget(lines, budget) {
|
|
|
29150
29247
|
|
|
29151
29248
|
// src/session/server-alive.ts
|
|
29152
29249
|
init_esm_shims();
|
|
29153
|
-
import { existsSync as
|
|
29154
|
-
import { join as
|
|
29250
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readdirSync, rmSync, writeFileSync as writeFileSync3 } from "fs";
|
|
29251
|
+
import { join as join8 } from "path";
|
|
29155
29252
|
function markersDir(ctx) {
|
|
29156
|
-
return
|
|
29253
|
+
return join8(projectDir(ctx.id), "servers");
|
|
29157
29254
|
}
|
|
29158
29255
|
function processAlive(pid) {
|
|
29159
29256
|
try {
|
|
@@ -29165,13 +29262,13 @@ function processAlive(pid) {
|
|
|
29165
29262
|
}
|
|
29166
29263
|
function liveServers(ctx) {
|
|
29167
29264
|
const dir = markersDir(ctx);
|
|
29168
|
-
if (!
|
|
29265
|
+
if (!existsSync7(dir)) return [];
|
|
29169
29266
|
const live2 = [];
|
|
29170
29267
|
for (const name of readdirSync(dir)) {
|
|
29171
29268
|
const pid = Number(name);
|
|
29172
29269
|
if (!Number.isInteger(pid) || pid <= 0) continue;
|
|
29173
29270
|
if (processAlive(pid)) live2.push(pid);
|
|
29174
|
-
else rmSync(
|
|
29271
|
+
else rmSync(join8(dir, name), { force: true });
|
|
29175
29272
|
}
|
|
29176
29273
|
return live2;
|
|
29177
29274
|
}
|
|
@@ -29181,30 +29278,30 @@ function serverAlive(ctx) {
|
|
|
29181
29278
|
|
|
29182
29279
|
// src/hooks/coexist.ts
|
|
29183
29280
|
init_esm_shims();
|
|
29184
|
-
import { readFileSync as
|
|
29185
|
-
import { join as
|
|
29281
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
29282
|
+
import { join as join10 } from "path";
|
|
29186
29283
|
|
|
29187
29284
|
// src/install/claude-config.ts
|
|
29188
29285
|
init_esm_shims();
|
|
29189
29286
|
import {
|
|
29190
29287
|
copyFileSync,
|
|
29191
|
-
existsSync as
|
|
29288
|
+
existsSync as existsSync8,
|
|
29192
29289
|
mkdirSync as mkdirSync5,
|
|
29193
29290
|
readdirSync as readdirSync2,
|
|
29194
|
-
readFileSync as
|
|
29291
|
+
readFileSync as readFileSync7,
|
|
29195
29292
|
writeFileSync as writeFileSync4
|
|
29196
29293
|
} from "fs";
|
|
29197
|
-
import { homedir as
|
|
29198
|
-
import { dirname as dirname4, join as
|
|
29199
|
-
var claudeDir = () => process.env.CLAUDE_CONFIG_DIR ||
|
|
29200
|
-
var pluginsDir = () =>
|
|
29294
|
+
import { homedir as homedir3 } from "os";
|
|
29295
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
29296
|
+
var claudeDir = () => process.env.CLAUDE_CONFIG_DIR || join9(homedir3(), ".claude");
|
|
29297
|
+
var pluginsDir = () => join9(claudeDir(), "plugins");
|
|
29201
29298
|
|
|
29202
29299
|
// src/hooks/coexist.ts
|
|
29203
29300
|
var cached2;
|
|
29204
29301
|
function contextModeInstalled() {
|
|
29205
29302
|
if (cached2 !== void 0) return cached2;
|
|
29206
29303
|
try {
|
|
29207
|
-
const reg = JSON.parse(
|
|
29304
|
+
const reg = JSON.parse(readFileSync8(join10(pluginsDir(), "installed_plugins.json"), "utf8"));
|
|
29208
29305
|
cached2 = Object.keys(reg.plugins ?? {}).some((id) => id.startsWith("context-mode@"));
|
|
29209
29306
|
} catch {
|
|
29210
29307
|
cached2 = false;
|
|
@@ -29259,7 +29356,7 @@ function coverageHint(db, rel) {
|
|
|
29259
29356
|
}
|
|
29260
29357
|
function docsSources(root) {
|
|
29261
29358
|
try {
|
|
29262
|
-
return readManifestAt(
|
|
29359
|
+
return readManifestAt(join11(root, ".eminiar", "docs.json")).sources;
|
|
29263
29360
|
} catch {
|
|
29264
29361
|
return [];
|
|
29265
29362
|
}
|
|
@@ -29318,9 +29415,11 @@ function suggest(toolName, input2, root) {
|
|
|
29318
29415
|
const fp = String(input2.file_path ?? "");
|
|
29319
29416
|
if (!fp) return void 0;
|
|
29320
29417
|
try {
|
|
29321
|
-
const
|
|
29418
|
+
const abs = resolve3(root, fp);
|
|
29419
|
+
const st = statSync4(abs);
|
|
29322
29420
|
const ext = extname2(fp).toLowerCase();
|
|
29323
29421
|
if (st.size > 200 * 1024 || BIG_TEXT_EXT.has(ext) && st.size > 20 * 1024) {
|
|
29422
|
+
if (!checkReadPath(loadPermissionRules(root), abs).allowed) return void 0;
|
|
29324
29423
|
return {
|
|
29325
29424
|
tool: "em_execute_file",
|
|
29326
29425
|
reason: `${fp} is ${Math.round(st.size / 1024)} KB; em_execute_file {path, code} processes it in the sandbox so only what you print enters context`
|
|
@@ -29372,7 +29471,7 @@ function projectRel(ctx, fp) {
|
|
|
29372
29471
|
if (!fp) return void 0;
|
|
29373
29472
|
let abs = resolve3(ctx.root, fp);
|
|
29374
29473
|
try {
|
|
29375
|
-
abs =
|
|
29474
|
+
abs = realpathSync4(abs);
|
|
29376
29475
|
} catch {
|
|
29377
29476
|
}
|
|
29378
29477
|
const rel = relative4(ctx.root, abs).split("\\").join("/");
|
|
@@ -29573,15 +29672,15 @@ function annotate(s, note = "") {
|
|
|
29573
29672
|
|
|
29574
29673
|
// src/hooks/session-start.ts
|
|
29575
29674
|
init_esm_shims();
|
|
29576
|
-
import { appendFileSync as appendFileSync2, existsSync as
|
|
29577
|
-
import { join as
|
|
29675
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync9, readdirSync as readdirSync3 } from "fs";
|
|
29676
|
+
import { join as join13 } from "path";
|
|
29578
29677
|
|
|
29579
29678
|
// src/session/current.ts
|
|
29580
29679
|
init_esm_shims();
|
|
29581
|
-
import { readFileSync as
|
|
29582
|
-
import { join as
|
|
29680
|
+
import { readFileSync as readFileSync9, statSync as statSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
29681
|
+
import { join as join12 } from "path";
|
|
29583
29682
|
function file2(ctx) {
|
|
29584
|
-
return
|
|
29683
|
+
return join12(projectDir(ctx.id), "current-session");
|
|
29585
29684
|
}
|
|
29586
29685
|
function setCurrentSession(ctx, sessionId) {
|
|
29587
29686
|
writeFileSync5(file2(ctx), sessionId);
|
|
@@ -29664,8 +29763,8 @@ ${guide}`);
|
|
|
29664
29763
|
} catch {
|
|
29665
29764
|
}
|
|
29666
29765
|
try {
|
|
29667
|
-
const onboarding =
|
|
29668
|
-
const onboarded =
|
|
29766
|
+
const onboarding = join13(memoriesDir(ctx), "onboarding");
|
|
29767
|
+
const onboarded = existsSync9(onboarding) && readdirSync3(onboarding).some((f) => f.endsWith(".md"));
|
|
29669
29768
|
if (!onboarded)
|
|
29670
29769
|
parts.push(
|
|
29671
29770
|
"No onboarding memories yet: run /eminiar:em-onboard once so future sessions start with the project's structure, build and test commands."
|
package/dist/server.js
CHANGED
|
@@ -51937,7 +51937,7 @@ import { join as join17 } from "path";
|
|
|
51937
51937
|
|
|
51938
51938
|
// src/permissions/settings.ts
|
|
51939
51939
|
init_esm_shims();
|
|
51940
|
-
import { existsSync as existsSync10, readFileSync as readFileSync11, statSync as statSync8 } from "fs";
|
|
51940
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11, realpathSync as realpathSync2, statSync as statSync8 } from "fs";
|
|
51941
51941
|
import { homedir as homedir2 } from "os";
|
|
51942
51942
|
import { join as join15 } from "path";
|
|
51943
51943
|
|
|
@@ -51978,15 +51978,24 @@ function matchBash(rule, command) {
|
|
|
51978
51978
|
if (rule.pattern === void 0) return true;
|
|
51979
51979
|
return bashRegex(rule.pattern).test(command);
|
|
51980
51980
|
}
|
|
51981
|
+
var readRegexCache = /* @__PURE__ */ new Map();
|
|
51982
|
+
function readRegex(pattern2) {
|
|
51983
|
+
let re = readRegexCache.get(pattern2);
|
|
51984
|
+
if (!re) {
|
|
51985
|
+
if (readRegexCache.size >= 1024) readRegexCache.clear();
|
|
51986
|
+
re = new RegExp(
|
|
51987
|
+
`^${pattern2.split("**").map((seg) => seg.split("*").map(escapeRe2).join("[^/]*")).join(".*")}$`
|
|
51988
|
+
);
|
|
51989
|
+
readRegexCache.set(pattern2, re);
|
|
51990
|
+
}
|
|
51991
|
+
return re;
|
|
51992
|
+
}
|
|
51981
51993
|
function matchRead(rule, absPath) {
|
|
51982
51994
|
if (rule.tool !== "Read") return false;
|
|
51983
51995
|
if (rule.pattern === void 0) return true;
|
|
51984
51996
|
let p = rule.pattern;
|
|
51985
51997
|
if (p.startsWith("~")) p = (process.env.HOME ?? "") + p.slice(1);
|
|
51986
|
-
|
|
51987
|
-
`^${p.split("**").map((seg) => seg.split("*").map(escapeRe2).join("[^/]*")).join(".*")}$`
|
|
51988
|
-
);
|
|
51989
|
-
return re.test(absPath);
|
|
51998
|
+
return readRegex(p).test(absPath);
|
|
51990
51999
|
}
|
|
51991
52000
|
|
|
51992
52001
|
// src/permissions/split.ts
|
|
@@ -52148,6 +52157,17 @@ function checkCommand(rules, command) {
|
|
|
52148
52157
|
}
|
|
52149
52158
|
return { allowed: true };
|
|
52150
52159
|
}
|
|
52160
|
+
function checkRead(rules, absPaths) {
|
|
52161
|
+
for (const kind of ["deny", "ask"]) {
|
|
52162
|
+
for (const r of rules[kind]) {
|
|
52163
|
+
if (absPaths.some((p) => matchRead(r, p))) return { allowed: false, rule: r.raw, kind };
|
|
52164
|
+
}
|
|
52165
|
+
}
|
|
52166
|
+
return { allowed: true };
|
|
52167
|
+
}
|
|
52168
|
+
function checkReadPath(rules, abs) {
|
|
52169
|
+
return checkRead(rules, [.../* @__PURE__ */ new Set([abs, realpathSync2(abs), realpathSync2.native(abs)])]);
|
|
52170
|
+
}
|
|
52151
52171
|
|
|
52152
52172
|
// src/sandbox/env.ts
|
|
52153
52173
|
init_esm_shims();
|
|
@@ -57097,16 +57117,18 @@ init_esm_shims();
|
|
|
57097
57117
|
|
|
57098
57118
|
// src/sandbox/file.ts
|
|
57099
57119
|
init_esm_shims();
|
|
57100
|
-
import { realpathSync as
|
|
57120
|
+
import { realpathSync as realpathSync3 } from "fs";
|
|
57101
57121
|
import { isAbsolute, relative as relative9, resolve as resolve7 } from "path";
|
|
57102
57122
|
function confinePath(root, target, rules) {
|
|
57103
57123
|
const abs = resolve7(root, target);
|
|
57124
|
+
refuse(checkRead(rules, [abs]), target);
|
|
57104
57125
|
let real;
|
|
57105
57126
|
try {
|
|
57106
|
-
real =
|
|
57127
|
+
real = realpathSync3(abs);
|
|
57107
57128
|
} catch {
|
|
57108
57129
|
throw new Error(`file not found: ${target}`);
|
|
57109
57130
|
}
|
|
57131
|
+
refuse(checkReadPath(rules, abs), target);
|
|
57110
57132
|
const rel = relative9(root, real);
|
|
57111
57133
|
const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
57112
57134
|
if (inside) return real;
|
|
@@ -57115,11 +57137,15 @@ function confinePath(root, target, rules) {
|
|
|
57115
57137
|
`${target} is outside the project root; add a Read(${real}) rule to permissions.allow to opt it in`
|
|
57116
57138
|
);
|
|
57117
57139
|
}
|
|
57140
|
+
function refuse(check3, target) {
|
|
57141
|
+
if (!check3.allowed)
|
|
57142
|
+
throw new Error(`refused by permission rule ${check3.rule} (${check3.kind}) on path: ${target}`);
|
|
57143
|
+
}
|
|
57118
57144
|
|
|
57119
57145
|
// src/tools/em_execute_file.ts
|
|
57120
57146
|
var emExecuteFile = defineTool({
|
|
57121
57147
|
name: "em_execute_file",
|
|
57122
|
-
description: "Process a file in the sandbox without reading it into context. Your code receives the absolute path as $EM_FILE (shell) / process.env.EM_FILE (node) / os.environ['EM_FILE'] (python); print only what you need. The file must be inside the project root unless a Read(<path>) allow rule opts it in. Omit `code` to get a quick summary (size, lines, head).",
|
|
57148
|
+
description: "Process a file in the sandbox without reading it into context. Your code receives the absolute path as $EM_FILE (shell) / process.env.EM_FILE (node) / os.environ['EM_FILE'] (python); print only what you need. The file must be inside the project root unless a Read(<path>) allow rule opts it in; a matching Read deny or ask rule refuses the path, inside the root or out. Omit `code` to get a quick summary (size, lines, head).",
|
|
57123
57149
|
schema: {
|
|
57124
57150
|
path: external_exports.string().describe("File to process, relative to the project root or absolute"),
|
|
57125
57151
|
language: LANG_ENUM.optional(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eminiar",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "Local-first context, memory and knowledge-graph plugin for Claude Code. Sandboxed execution, FTS5 + vector search, session continuity, and a code graph — nothing leaves your machine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|