@rubytech/create-realagent 1.0.705 → 1.0.707
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/__tests__/apt-resolve.test.js +179 -0
- package/dist/apt-resolve.js +73 -0
- package/dist/index.js +48 -46
- package/package.json +3 -3
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js +89 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cypher-parser.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts +42 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js +87 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cypher-parser.js.map +1 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/schema-cypher-parser.test.ts +99 -0
- package/payload/platform/lib/graph-mcp/src/schema-cypher-parser.ts +84 -0
- package/payload/platform/neo4j/schema.cypher +23 -0
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +30 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/business-profile/SKILL.md +2 -2
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +47 -6
- package/payload/platform/plugins/docs/references/adherence.md +1 -1
- package/payload/platform/plugins/memory/PLUGIN.md +25 -16
- package/payload/platform/plugins/memory/mcp/dist/index.js +146 -38
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js +92 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/live-schema-source.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js +51 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-loader.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js +222 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/schema-validator.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts +1 -7
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js +27 -14
- package/payload/platform/plugins/memory/mcp/dist/lib/document-hierarchy.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts +16 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js +38 -11
- package/payload/platform/plugins/memory/mcp/dist/lib/graph-write-gate.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts +136 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js +180 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/live-schema-source.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts +126 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js +253 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/llm-classifier.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts +11 -2
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js +6 -3
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-loader.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts +44 -22
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js +94 -57
- package/payload/platform/plugins/memory/mcp/dist/lib/schema-validator.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts +34 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js +46 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-classify.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts +1 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js +8 -9
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-edit-attachment.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts +5 -17
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js +26 -49
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-extract.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js +4 -25
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest-web.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts +23 -14
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +410 -164
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +7 -5
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +2 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +33 -0
- package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +112 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -2
- package/payload/platform/templates/specialists/agents/content-producer.md +10 -77
- package/payload/platform/templates/specialists/agents/database-operator.md +21 -13
- package/payload/server/chunk-PE76FPYP.js +12040 -0
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{Checkbox-B2Lk8F4X.js → Checkbox-CjbS9JcG.js} +1 -1
- package/payload/server/public/assets/{admin-agtgi48Q.js → admin-Ce9DbUuu.js} +1 -1
- package/payload/server/public/assets/{data-B7nsyBTV.js → data-C-SxjLC9.js} +1 -1
- package/payload/server/public/assets/{file-DHWTu8LP.js → file-D4cbAAuo.js} +1 -1
- package/payload/server/public/assets/{graph-ChDwqqhJ.js → graph-BRD96pKD.js} +8 -8
- package/payload/server/public/assets/{house-CfjnRPO6.js → house-CYsVygEQ.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-81wg0w0Q.css → jsx-runtime-DPXE45W9.css} +1 -1
- package/payload/server/public/assets/{public-CE1kyVnz.js → public-BTOF98iO.js} +1 -1
- package/payload/server/public/assets/{share-2-CAd1beVT.js → share-2-B-sbkB36.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-LSAU68Eo.js → useVoiceRecorder-DLVFx3ms.js} +1 -1
- package/payload/server/public/assets/{x-B0xK3Aoq.js → x-BNidzSAn.js} +1 -1
- package/payload/server/public/data.html +6 -6
- package/payload/server/public/graph.html +7 -7
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +6 -10
- /package/payload/server/public/assets/{jsx-runtime-DhzH26q8.js → jsx-runtime-BUs3sHtV.js} +0 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Task 638 — acceptance grid for apt-resolve.ts.
|
|
2
|
+
//
|
|
3
|
+
// Locks the four resolver branches from Task 637 plus the os-release parser
|
|
4
|
+
// and Candidate-line extractor. Inputs are passed directly so no mocking is
|
|
5
|
+
// needed — every test exercises pure logic with concrete fixtures captured
|
|
6
|
+
// from real devices (Noble `apt-cache policy chromium`, Bookworm ditto,
|
|
7
|
+
// Pop!_OS `/etc/os-release`).
|
|
8
|
+
//
|
|
9
|
+
// Runs via Node's built-in test runner; no vitest dependency. Compiles to
|
|
10
|
+
// dist/__tests__/apt-resolve.test.js alongside the rest of the package so
|
|
11
|
+
// `node --test dist/__tests__/*.test.js` picks it up after build.
|
|
12
|
+
import test from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import { UBUNTU_ALIASES, parseOsRelease, isUbuntuLike, parseAptCacheCandidate, decideAptResolution, } from "../apt-resolve.js";
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Real fixtures — captured from devices the installer actually targets.
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
const UBUNTU_NOBLE_OS_RELEASE = `PRETTY_NAME="Ubuntu 24.04 LTS"
|
|
19
|
+
NAME="Ubuntu"
|
|
20
|
+
VERSION_ID="24.04"
|
|
21
|
+
VERSION="24.04 LTS (Noble Numbat)"
|
|
22
|
+
VERSION_CODENAME=noble
|
|
23
|
+
ID=ubuntu
|
|
24
|
+
ID_LIKE=debian
|
|
25
|
+
HOME_URL="https://www.ubuntu.com/"
|
|
26
|
+
UBUNTU_CODENAME=noble`;
|
|
27
|
+
const DEBIAN_BOOKWORM_OS_RELEASE = `PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
|
|
28
|
+
NAME="Debian GNU/Linux"
|
|
29
|
+
VERSION_ID="12"
|
|
30
|
+
VERSION="12 (bookworm)"
|
|
31
|
+
VERSION_CODENAME=bookworm
|
|
32
|
+
ID=debian
|
|
33
|
+
HOME_URL="https://www.debian.org/"`;
|
|
34
|
+
const POP_OS_OS_RELEASE = `NAME="Pop!_OS"
|
|
35
|
+
VERSION="22.04 LTS"
|
|
36
|
+
ID=pop
|
|
37
|
+
ID_LIKE="ubuntu debian"
|
|
38
|
+
PRETTY_NAME="Pop!_OS 22.04 LTS"
|
|
39
|
+
VERSION_ID="22.04"
|
|
40
|
+
VERSION_CODENAME=jammy
|
|
41
|
+
UBUNTU_CODENAME=jammy`;
|
|
42
|
+
// `apt-cache policy chromium` on Ubuntu Noble (the alias case Task 637 fixed).
|
|
43
|
+
const NOBLE_CHROMIUM_POLICY = `chromium:
|
|
44
|
+
Installed: (none)
|
|
45
|
+
Candidate: (none)
|
|
46
|
+
Version table:`;
|
|
47
|
+
// `apt-cache policy chromium` on Debian Bookworm (concrete .deb, no alias).
|
|
48
|
+
const BOOKWORM_CHROMIUM_POLICY = `chromium:
|
|
49
|
+
Installed: (none)
|
|
50
|
+
Candidate: 120.0.6099.224-1~deb12u1
|
|
51
|
+
Version table:
|
|
52
|
+
120.0.6099.224-1~deb12u1 500
|
|
53
|
+
500 http://deb.debian.org/debian bookworm-security/main arm64 Packages`;
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// parseOsRelease — every key/value pair lands, quoted values get unquoted.
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
test("parseOsRelease unquotes values and captures all keys", () => {
|
|
58
|
+
const noble = parseOsRelease(UBUNTU_NOBLE_OS_RELEASE);
|
|
59
|
+
assert.equal(noble.ID, "ubuntu");
|
|
60
|
+
assert.equal(noble.ID_LIKE, "debian");
|
|
61
|
+
assert.equal(noble.VERSION_CODENAME, "noble");
|
|
62
|
+
assert.equal(noble.NAME, "Ubuntu", "double-quoted values must be unquoted");
|
|
63
|
+
assert.equal(noble.PRETTY_NAME, "Ubuntu 24.04 LTS");
|
|
64
|
+
});
|
|
65
|
+
test("parseOsRelease handles space-separated ID_LIKE (Pop!_OS)", () => {
|
|
66
|
+
const pop = parseOsRelease(POP_OS_OS_RELEASE);
|
|
67
|
+
assert.equal(pop.ID, "pop");
|
|
68
|
+
assert.equal(pop.ID_LIKE, "ubuntu debian", "quoted multi-value must be preserved as a single string");
|
|
69
|
+
});
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// isUbuntuLike — Ubuntu, Pop!_OS pick up; Debian does not.
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
test("isUbuntuLike accepts Ubuntu directly and via ID_LIKE", () => {
|
|
74
|
+
assert.equal(isUbuntuLike(parseOsRelease(UBUNTU_NOBLE_OS_RELEASE)), true, "Ubuntu Noble: ID=ubuntu");
|
|
75
|
+
assert.equal(isUbuntuLike(parseOsRelease(POP_OS_OS_RELEASE)), true, "Pop!_OS: ID_LIKE contains ubuntu");
|
|
76
|
+
assert.equal(isUbuntuLike(parseOsRelease(DEBIAN_BOOKWORM_OS_RELEASE)), false, "Debian Bookworm: not Ubuntu-like");
|
|
77
|
+
assert.equal(isUbuntuLike({}), false, "empty os-release (read failed): not Ubuntu-like");
|
|
78
|
+
});
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// parseAptCacheCandidate — three states: real candidate, "(none)", absent.
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
test("parseAptCacheCandidate distinguishes real candidate, (none), and missing", () => {
|
|
83
|
+
assert.equal(parseAptCacheCandidate(BOOKWORM_CHROMIUM_POLICY), "120.0.6099.224-1~deb12u1");
|
|
84
|
+
assert.equal(parseAptCacheCandidate(NOBLE_CHROMIUM_POLICY), "(none)");
|
|
85
|
+
assert.equal(parseAptCacheCandidate(""), null, "empty stdout (spawn failed) → null");
|
|
86
|
+
assert.equal(parseAptCacheCandidate("totally unrelated text"), null, "no Candidate line → null");
|
|
87
|
+
});
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
// decideAptResolution — the four resolver branches from Task 637.
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
test("decideAptResolution: dpkg-installed package returns pkg, no log", () => {
|
|
92
|
+
// Branch (1): dpkg -s exits 0 — name is concrete and installed.
|
|
93
|
+
const r = decideAptResolution({
|
|
94
|
+
pkg: "curl",
|
|
95
|
+
dpkgInstalled: true,
|
|
96
|
+
aptCandidate: null,
|
|
97
|
+
ubuntuLike: true,
|
|
98
|
+
distro: "ubuntu-noble",
|
|
99
|
+
});
|
|
100
|
+
assert.equal(r.resolved, "curl");
|
|
101
|
+
assert.equal(r.log, null);
|
|
102
|
+
});
|
|
103
|
+
test("decideAptResolution: real apt candidate returns pkg, no log (Bookworm chromium)", () => {
|
|
104
|
+
// Branch (2): apt-cache policy says there IS a real candidate — leave name
|
|
105
|
+
// alone, the post-check will use the same name.
|
|
106
|
+
const r = decideAptResolution({
|
|
107
|
+
pkg: "chromium",
|
|
108
|
+
dpkgInstalled: false,
|
|
109
|
+
aptCandidate: "120.0.6099.224-1~deb12u1",
|
|
110
|
+
ubuntuLike: false,
|
|
111
|
+
distro: "debian-bookworm",
|
|
112
|
+
});
|
|
113
|
+
assert.equal(r.resolved, "chromium");
|
|
114
|
+
assert.equal(r.log, null);
|
|
115
|
+
});
|
|
116
|
+
test("decideAptResolution: Noble chromium → chromium-browser with log line", () => {
|
|
117
|
+
// Branch (3): the load-bearing case Task 637 fixed.
|
|
118
|
+
const r = decideAptResolution({
|
|
119
|
+
pkg: "chromium",
|
|
120
|
+
dpkgInstalled: false,
|
|
121
|
+
aptCandidate: "(none)",
|
|
122
|
+
ubuntuLike: true,
|
|
123
|
+
distro: "ubuntu-noble",
|
|
124
|
+
});
|
|
125
|
+
assert.equal(r.resolved, "chromium-browser");
|
|
126
|
+
assert.match(r.log ?? "", /^ apt-resolve chromium → chromium-browser \(reason=candidate-none, distro=ubuntu-noble\)$/);
|
|
127
|
+
});
|
|
128
|
+
test("decideAptResolution: Bookworm + alias-keyed pkg + candidate-none returns pkg unchanged", () => {
|
|
129
|
+
// Branch (4) variant: not Ubuntu-like, so even an alias-keyed pkg falls
|
|
130
|
+
// through. Preserves Task 634's loud-failure contract — apt-get install
|
|
131
|
+
// will fail, post-check throws, operator sees the real diagnostic.
|
|
132
|
+
const r = decideAptResolution({
|
|
133
|
+
pkg: "chromium",
|
|
134
|
+
dpkgInstalled: false,
|
|
135
|
+
aptCandidate: "(none)",
|
|
136
|
+
ubuntuLike: false,
|
|
137
|
+
distro: "debian-bookworm",
|
|
138
|
+
});
|
|
139
|
+
assert.equal(r.resolved, "chromium");
|
|
140
|
+
assert.equal(r.log, null);
|
|
141
|
+
});
|
|
142
|
+
test("decideAptResolution: Ubuntu-like + non-aliased pkg + candidate-none returns pkg unchanged", () => {
|
|
143
|
+
// Branch (4) variant: Ubuntu-like host but the package isn't in the alias
|
|
144
|
+
// map. No invented mapping — fall through and let the post-check fail loudly.
|
|
145
|
+
const r = decideAptResolution({
|
|
146
|
+
pkg: "some-package-with-no-alias",
|
|
147
|
+
dpkgInstalled: false,
|
|
148
|
+
aptCandidate: "(none)",
|
|
149
|
+
ubuntuLike: true,
|
|
150
|
+
distro: "ubuntu-noble",
|
|
151
|
+
});
|
|
152
|
+
assert.equal(r.resolved, "some-package-with-no-alias");
|
|
153
|
+
assert.equal(r.log, null);
|
|
154
|
+
});
|
|
155
|
+
test("decideAptResolution: aptCandidate null (policy spawn failed) still allows alias resolution", () => {
|
|
156
|
+
// Edge case from Task 637: if `apt-cache policy` itself fails, we still want
|
|
157
|
+
// alias resolution on Ubuntu-like hosts. Treat null-candidate the same as
|
|
158
|
+
// candidate-none for the alias decision.
|
|
159
|
+
const r = decideAptResolution({
|
|
160
|
+
pkg: "chromium",
|
|
161
|
+
dpkgInstalled: false,
|
|
162
|
+
aptCandidate: null,
|
|
163
|
+
ubuntuLike: true,
|
|
164
|
+
distro: "ubuntu-noble",
|
|
165
|
+
});
|
|
166
|
+
assert.equal(r.resolved, "chromium-browser");
|
|
167
|
+
assert.match(r.log ?? "", /apt-resolve chromium → chromium-browser/);
|
|
168
|
+
});
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
170
|
+
// UBUNTU_ALIASES — the alias map is the authoritative source. Locking its
|
|
171
|
+
// shape so adding a new alias is a deliberate one-line PR, not a surprise.
|
|
172
|
+
// ---------------------------------------------------------------------------
|
|
173
|
+
test("UBUNTU_ALIASES contains chromium → chromium-browser and is the only entry", () => {
|
|
174
|
+
assert.equal(UBUNTU_ALIASES.chromium, "chromium-browser");
|
|
175
|
+
// If a future change adds another alias, this test fails loudly — the
|
|
176
|
+
// intent is "every new alias gets a deliberate test grid case", not silent
|
|
177
|
+
// expansion. Bump the count and add cases when adding a new alias.
|
|
178
|
+
assert.equal(Object.keys(UBUNTU_ALIASES).length, 1);
|
|
179
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Task 638 — pure apt-name resolution, extracted from index.ts so a unit
|
|
2
|
+
// test can exercise every branch without spawning real apt/dpkg or reading
|
|
3
|
+
// /etc/os-release. Mirrors the port-resolution.ts pattern from Task 666:
|
|
4
|
+
// inputs in, decision out, no I/O. The installer wraps this with the actual
|
|
5
|
+
// spawnSync calls and side-effecting logFile.
|
|
6
|
+
//
|
|
7
|
+
// The original logic landed in Task 637 (chromium-noble alias). This file
|
|
8
|
+
// preserves that semantics verbatim — every branch maps 1:1.
|
|
9
|
+
// Virtual-package aliases applied when `apt-cache policy <pkg>` returns
|
|
10
|
+
// `Candidate: (none)`. Keyed by apt-level alias, valued by the real .deb
|
|
11
|
+
// name dpkg records post-install. Scoped per distro family by the caller's
|
|
12
|
+
// `ubuntuLike` input. Add entries here if a new alias class surfaces.
|
|
13
|
+
export const UBUNTU_ALIASES = {
|
|
14
|
+
// Noble ships `chromium` as a virtual package that resolves to
|
|
15
|
+
// `chromium-browser`, which is itself a transitional stub delegating to
|
|
16
|
+
// the snap. The runtime binary at /usr/bin/chromium is a snap symlink;
|
|
17
|
+
// dpkg records `chromium-browser` post-install, never `chromium`.
|
|
18
|
+
chromium: "chromium-browser",
|
|
19
|
+
};
|
|
20
|
+
/** Parse `/etc/os-release` content into a key→value map. */
|
|
21
|
+
export function parseOsRelease(raw) {
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const line of raw.split("\n")) {
|
|
24
|
+
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
25
|
+
if (m)
|
|
26
|
+
out[m[1]] = m[2].replace(/^"|"$/g, "");
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** True when the host is Ubuntu or Ubuntu-derivative (Pop!_OS, Mint, etc.). */
|
|
31
|
+
export function isUbuntuLike(os) {
|
|
32
|
+
if (os.ID === "ubuntu")
|
|
33
|
+
return true;
|
|
34
|
+
const likes = (os.ID_LIKE ?? "").split(/\s+/);
|
|
35
|
+
return likes.includes("ubuntu");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract the Candidate field from `apt-cache policy <pkg>` stdout. Returns
|
|
39
|
+
* the literal string after `Candidate:` (so `(none)` stays as `(none)` —
|
|
40
|
+
* meaningful to the resolver, not a magic value). Returns null when no
|
|
41
|
+
* Candidate line is present (spawn failure, malformed output, etc.).
|
|
42
|
+
*/
|
|
43
|
+
export function parseAptCacheCandidate(stdout) {
|
|
44
|
+
const m = stdout.match(/Candidate:\s*(\S+)/);
|
|
45
|
+
return m ? m[1] : null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Pure decision: given the dpkg/apt/os-release facts, decide which package
|
|
49
|
+
* name to use. Resolution order matches Task 637:
|
|
50
|
+
* 1. dpkg -s pkg succeeded → name is concrete and installed; return pkg.
|
|
51
|
+
* 2. apt-cache policy reports a real Candidate (not `(none)`) → return pkg.
|
|
52
|
+
* 3. Host is Ubuntu-like AND pkg is in UBUNTU_ALIASES → return the alias
|
|
53
|
+
* and emit the log line.
|
|
54
|
+
* 4. Otherwise return pkg unchanged — caller's post-check will throw,
|
|
55
|
+
* preserving Task 634's fail-loud contract for genuinely missing
|
|
56
|
+
* packages.
|
|
57
|
+
*/
|
|
58
|
+
export function decideAptResolution(input) {
|
|
59
|
+
const { pkg, dpkgInstalled, aptCandidate, ubuntuLike, distro } = input;
|
|
60
|
+
if (dpkgInstalled)
|
|
61
|
+
return { resolved: pkg, log: null };
|
|
62
|
+
if (aptCandidate && aptCandidate !== "(none)") {
|
|
63
|
+
return { resolved: pkg, log: null };
|
|
64
|
+
}
|
|
65
|
+
if (ubuntuLike && Object.prototype.hasOwnProperty.call(UBUNTU_ALIASES, pkg)) {
|
|
66
|
+
const alias = UBUNTU_ALIASES[pkg];
|
|
67
|
+
return {
|
|
68
|
+
resolved: alias,
|
|
69
|
+
log: ` apt-resolve ${pkg} → ${alias} (reason=candidate-none, distro=${distro})`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { resolved: pkg, log: null };
|
|
73
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, rmSync, rea
|
|
|
4
4
|
import { resolve, join, dirname } from "node:path";
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { resolveInstallPortFromFs, buildMaxyUnitFile } from "./port-resolution.js";
|
|
7
|
+
import { parseOsRelease, isUbuntuLike as isUbuntuLikePure, parseAptCacheCandidate, decideAptResolution, } from "./apt-resolve.js";
|
|
7
8
|
const PAYLOAD_DIR = resolve(import.meta.dirname, "../payload");
|
|
8
9
|
// Brand manifest — read from payload to derive all brand-specific installation values.
|
|
9
10
|
// The bundler stamps brand.json into the payload at build time.
|
|
@@ -25,6 +26,14 @@ catch (err) {
|
|
|
25
26
|
const INSTALL_DIR = resolve(process.env.HOME ?? "/root", BRAND.installDir);
|
|
26
27
|
const LOG_DIR = resolve(process.env.HOME ?? "/root", BRAND.configDir, "logs");
|
|
27
28
|
const LOG_FILE = join(LOG_DIR, `install-${new Date().toISOString().replace(/[:.]/g, "-")}.log`);
|
|
29
|
+
/** Known brand hostnames in the Maxy ecosystem. Each brand ships a main unit
|
|
30
|
+
* (`<hostname>.service`) and a per-brand edge unit (`<hostname>-edge.service`,
|
|
31
|
+
* Task 662). Peer-brand detection matches only these filenames — stale units,
|
|
32
|
+
* gnome-keyring disable markers, and unrelated user services are not peer
|
|
33
|
+
* evidence. When a third brand is added under `brands/`, append its hostname
|
|
34
|
+
* here AND in the matching constant in `uninstall.ts` (intentional duplication
|
|
35
|
+
* per `uninstall.ts:` "Shell helpers (duplicated from index.ts ...)" policy). */
|
|
36
|
+
const KNOWN_BRAND_HOSTNAMES = ["maxy", "realagent"];
|
|
28
37
|
// The device's actual hostname — may differ from BRAND.hostname if the user customized it.
|
|
29
38
|
// Updated by installSystemDeps() after hostname setup; used for user-facing URLs.
|
|
30
39
|
let DEVICE_HOSTNAME = BRAND.hostname;
|
|
@@ -210,50 +219,28 @@ function canSudo() {
|
|
|
210
219
|
// Task 637 — resolve package-name aliases before probing dpkg, so the
|
|
211
220
|
// post-install check no longer false-negatives when apt resolves a virtual
|
|
212
221
|
// name (e.g. Noble's `chromium` → `chromium-browser`).
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
const UBUNTU_ALIASES = {
|
|
219
|
-
// Noble ships `chromium` as a virtual package that resolves to
|
|
220
|
-
// `chromium-browser`, which is itself a transitional stub delegating to
|
|
221
|
-
// the snap. The runtime binary at /usr/bin/chromium is a snap symlink;
|
|
222
|
-
// dpkg records `chromium-browser` post-install, never `chromium`.
|
|
223
|
-
chromium: "chromium-browser",
|
|
224
|
-
};
|
|
222
|
+
// UBUNTU_ALIASES, parseOsRelease, isUbuntuLike, and decideAptResolution moved
|
|
223
|
+
// to ./apt-resolve.ts (Task 638). The pure logic lives there so a unit test
|
|
224
|
+
// can hit every branch without spawning real apt/dpkg or reading
|
|
225
|
+
// /etc/os-release. The thin wrappers below feed real spawn + fs results into
|
|
226
|
+
// that pure decision.
|
|
225
227
|
function readOsRelease() {
|
|
226
228
|
try {
|
|
227
|
-
|
|
228
|
-
const out = {};
|
|
229
|
-
for (const line of raw.split("\n")) {
|
|
230
|
-
const m = line.match(/^([A-Z_]+)=(.*)$/);
|
|
231
|
-
if (m)
|
|
232
|
-
out[m[1]] = m[2].replace(/^"|"$/g, "");
|
|
233
|
-
}
|
|
234
|
-
return out;
|
|
229
|
+
return parseOsRelease(readFileSync("/etc/os-release", "utf-8"));
|
|
235
230
|
}
|
|
236
231
|
catch {
|
|
237
232
|
return {};
|
|
238
233
|
}
|
|
239
234
|
}
|
|
240
|
-
function isUbuntuLike() {
|
|
241
|
-
const os = readOsRelease();
|
|
242
|
-
if (os.ID === "ubuntu")
|
|
243
|
-
return true;
|
|
244
|
-
const likes = (os.ID_LIKE ?? "").split(/\s+/);
|
|
245
|
-
return likes.includes("ubuntu");
|
|
246
|
-
}
|
|
247
235
|
/** Summarise `apt-cache policy` output for diagnostics — one token per package. */
|
|
248
236
|
function aptCachePolicySummary(pkg) {
|
|
249
237
|
const r = spawnSync("apt-cache", ["policy", pkg], { stdio: "pipe", encoding: "utf-8", timeout: 5_000 });
|
|
250
238
|
if (r.status !== 0)
|
|
251
239
|
return "policy-spawn-failed";
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
if (!cand)
|
|
240
|
+
const cand = parseAptCacheCandidate(r.stdout ?? "");
|
|
241
|
+
if (cand === null)
|
|
255
242
|
return "no-candidate-line";
|
|
256
|
-
return cand
|
|
243
|
+
return cand === "(none)" ? "candidate-none" : `candidate=${cand}`;
|
|
257
244
|
}
|
|
258
245
|
/**
|
|
259
246
|
* Task 637 — map an apt-level package name to the concrete name dpkg will
|
|
@@ -269,24 +256,30 @@ function aptCachePolicySummary(pkg) {
|
|
|
269
256
|
*/
|
|
270
257
|
function resolveAptName(pkg) {
|
|
271
258
|
const dpkg = spawnSync("dpkg", ["-s", pkg], { stdio: "pipe", timeout: 5_000 });
|
|
272
|
-
|
|
259
|
+
const dpkgInstalled = dpkg.status === 0;
|
|
260
|
+
// Short-circuit: when dpkg already records the name as installed, skip the
|
|
261
|
+
// apt-cache + os-release work — `decideAptResolution` returns pkg unchanged
|
|
262
|
+
// anyway and the extra spawn would burn ~10 ms per already-installed
|
|
263
|
+
// package across every `pkgsMissing` pass.
|
|
264
|
+
if (dpkgInstalled)
|
|
273
265
|
return pkg;
|
|
274
266
|
const policy = spawnSync("apt-cache", ["policy", pkg], {
|
|
275
267
|
stdio: "pipe", encoding: "utf-8", timeout: 5_000,
|
|
276
268
|
});
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
269
|
+
const aptCandidate = policy.status === 0
|
|
270
|
+
? parseAptCacheCandidate(policy.stdout ?? "")
|
|
271
|
+
: null;
|
|
272
|
+
const os = readOsRelease();
|
|
273
|
+
const decision = decideAptResolution({
|
|
274
|
+
pkg,
|
|
275
|
+
dpkgInstalled,
|
|
276
|
+
aptCandidate,
|
|
277
|
+
ubuntuLike: isUbuntuLikePure(os),
|
|
278
|
+
distro: `${os.ID ?? "unknown"}-${os.VERSION_CODENAME ?? "unknown"}`,
|
|
279
|
+
});
|
|
280
|
+
if (decision.log)
|
|
281
|
+
logFile(decision.log);
|
|
282
|
+
return decision.resolved;
|
|
290
283
|
}
|
|
291
284
|
/** Probe runtime binary presence on PATH (independent of dpkg-recorded state). */
|
|
292
285
|
function commandVPath(pkg) {
|
|
@@ -432,10 +425,19 @@ function installSystemDeps() {
|
|
|
432
425
|
// set by the user or the first installer — subsequent brands must not overwrite it.
|
|
433
426
|
const systemdUserDir = resolve(process.env.HOME ?? "/root", ".config/systemd/user");
|
|
434
427
|
const serviceExists = existsSync(join(systemdUserDir, BRAND.serviceName));
|
|
428
|
+
// Task 690: narrow peer detection to KNOWN_BRAND_HOSTNAMES. The previous
|
|
429
|
+
// predicate ("any *.service that isn't ours") matched stray user units
|
|
430
|
+
// such as a `gnome-keyring-daemon.service -> /dev/null` disable marker,
|
|
431
|
+
// silently flipping single-brand fresh installs into the "preserve hostname"
|
|
432
|
+
// branch. Mirrors Task 683's `peerBrandPresent` allowlist in `uninstall.ts`.
|
|
435
433
|
let otherBrandService = false;
|
|
436
434
|
if (!serviceExists) {
|
|
435
|
+
const peerUnits = KNOWN_BRAND_HOSTNAMES
|
|
436
|
+
.filter((h) => h !== BRAND.hostname)
|
|
437
|
+
.flatMap((h) => [`${h}.service`, `${h}-edge.service`]);
|
|
437
438
|
try {
|
|
438
|
-
|
|
439
|
+
const files = new Set(readdirSync(systemdUserDir));
|
|
440
|
+
otherBrandService = peerUnits.some((unit) => files.has(unit));
|
|
439
441
|
}
|
|
440
442
|
catch { /* directory may not exist on a fresh device — not an error */ }
|
|
441
443
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rubytech/create-realagent",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.707",
|
|
4
4
|
"description": "Install Real Agent — Built for agents. By agents.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-realagent": "./dist/index.js"
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"bundle": "node scripts/bundle.js",
|
|
12
|
-
"test": "npm run build && node --test dist/__tests__
|
|
13
|
-
"prepublishOnly": "node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && chmod +x dist/index.js && npm run bundle && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
|
|
12
|
+
"test": "npm run build && node --test 'dist/__tests__/*.test.js'",
|
|
13
|
+
"prepublishOnly": "node ../../platform/ui/scripts/check-route-wiring.mjs && node ../../platform/ui/scripts/check-edge-admin-routes.mjs && npm run build && node --test 'dist/__tests__/*.test.js' && chmod +x dist/index.js && npm run bundle && node ../../platform/ui/scripts/check-bundle-node-imports.mjs --dir=./payload/server/public/assets"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-cypher-parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema-cypher-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_test_1 = __importDefault(require("node:test"));
|
|
7
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
8
|
+
const schema_cypher_parser_js_1 = require("../schema-cypher-parser.js");
|
|
9
|
+
(0, node_test_1.default)("parseLabelsFromSchemaCypher: extracts labels from constraint and index forms", () => {
|
|
10
|
+
const text = `
|
|
11
|
+
CREATE CONSTRAINT person_email IF NOT EXISTS
|
|
12
|
+
FOR (p:Person) REQUIRE p.email IS UNIQUE;
|
|
13
|
+
|
|
14
|
+
CREATE CONSTRAINT business_account IF NOT EXISTS
|
|
15
|
+
FOR (b:LocalBusiness) REQUIRE b.accountId IS UNIQUE;
|
|
16
|
+
|
|
17
|
+
CREATE INDEX admin_user_account IF NOT EXISTS
|
|
18
|
+
FOR (au:AdminUser) ON (au.accountId);
|
|
19
|
+
|
|
20
|
+
CREATE INDEX knowledge_doc IF NOT EXISTS
|
|
21
|
+
FOR (k:KnowledgeDocument) ON (k.embedding) OPTIONS { ... };
|
|
22
|
+
`;
|
|
23
|
+
const labels = (0, schema_cypher_parser_js_1.parseLabelsFromSchemaCypher)(text);
|
|
24
|
+
strict_1.default.deepEqual(labels, [
|
|
25
|
+
"AdminUser",
|
|
26
|
+
"KnowledgeDocument",
|
|
27
|
+
"LocalBusiness",
|
|
28
|
+
"Person",
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
(0, node_test_1.default)("parseLabelsFromSchemaCypher: deduplicates labels declared multiple times", () => {
|
|
32
|
+
const text = `
|
|
33
|
+
FOR (p:Person) REQUIRE p.email IS UNIQUE;
|
|
34
|
+
FOR (p:Person) REQUIRE p.telephone IS UNIQUE;
|
|
35
|
+
FOR (p:Person) ON (p.status);
|
|
36
|
+
`;
|
|
37
|
+
strict_1.default.deepEqual((0, schema_cypher_parser_js_1.parseLabelsFromSchemaCypher)(text), ["Person"]);
|
|
38
|
+
});
|
|
39
|
+
(0, node_test_1.default)("parseLabelsFromSchemaCypher: returns sorted output", () => {
|
|
40
|
+
const text = `FOR (z:Zebra) ON (z.x); FOR (a:Aardvark) ON (a.x); FOR (m:Mongoose) ON (m.x);`;
|
|
41
|
+
strict_1.default.deepEqual((0, schema_cypher_parser_js_1.parseLabelsFromSchemaCypher)(text), [
|
|
42
|
+
"Aardvark",
|
|
43
|
+
"Mongoose",
|
|
44
|
+
"Zebra",
|
|
45
|
+
]);
|
|
46
|
+
});
|
|
47
|
+
(0, node_test_1.default)("parseLabelsFromSchemaCypher: ignores non-constraint cypher", () => {
|
|
48
|
+
const text = `
|
|
49
|
+
MATCH (n:Person) RETURN n;
|
|
50
|
+
CREATE (b:Business { name: 'X' });
|
|
51
|
+
FOR (p:Person) REQUIRE p.email IS UNIQUE;
|
|
52
|
+
`;
|
|
53
|
+
// MATCH/CREATE patterns don't include FOR, so they're ignored.
|
|
54
|
+
strict_1.default.deepEqual((0, schema_cypher_parser_js_1.parseLabelsFromSchemaCypher)(text), ["Person"]);
|
|
55
|
+
});
|
|
56
|
+
(0, node_test_1.default)("parseLabelsFromSchemaCypher: empty input returns empty array", () => {
|
|
57
|
+
strict_1.default.deepEqual((0, schema_cypher_parser_js_1.parseLabelsFromSchemaCypher)(""), []);
|
|
58
|
+
});
|
|
59
|
+
(0, node_test_1.default)("levenshtein: identical strings", () => {
|
|
60
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("Person", "Person"), 0);
|
|
61
|
+
});
|
|
62
|
+
(0, node_test_1.default)("levenshtein: substitution distance", () => {
|
|
63
|
+
// LocaIBusiness vs LocalBusiness — capital-I-as-l typo from the incident.
|
|
64
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("LocaIBusiness", "LocalBusiness"), 1);
|
|
65
|
+
});
|
|
66
|
+
(0, node_test_1.default)("levenshtein: insertion + deletion", () => {
|
|
67
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("Person", "Persons"), 1);
|
|
68
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("Persons", "Person"), 1);
|
|
69
|
+
});
|
|
70
|
+
(0, node_test_1.default)("levenshtein: handles empty strings", () => {
|
|
71
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("", "abc"), 3);
|
|
72
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("abc", ""), 3);
|
|
73
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.levenshtein)("", ""), 0);
|
|
74
|
+
});
|
|
75
|
+
(0, node_test_1.default)("nearestLabel: finds the closest match", () => {
|
|
76
|
+
const candidates = ["Person", "LocalBusiness", "AdminUser"];
|
|
77
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.nearestLabel)("LocaIBusiness", candidates), "LocalBusiness");
|
|
78
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.nearestLabel)("AdminUsr", candidates), "AdminUser");
|
|
79
|
+
});
|
|
80
|
+
(0, node_test_1.default)("nearestLabel: returns null when no match within maxDistance", () => {
|
|
81
|
+
const candidates = ["Person", "LocalBusiness"];
|
|
82
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.nearestLabel)("Quokka", candidates), null);
|
|
83
|
+
// Default maxDistance is 3; "abcde" is too far from any candidate.
|
|
84
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.nearestLabel)("abcde", candidates, 1), null);
|
|
85
|
+
});
|
|
86
|
+
(0, node_test_1.default)("nearestLabel: empty candidates returns null", () => {
|
|
87
|
+
strict_1.default.equal((0, schema_cypher_parser_js_1.nearestLabel)("Person", []), null);
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=schema-cypher-parser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-cypher-parser.test.js","sourceRoot":"","sources":["../../src/__tests__/schema-cypher-parser.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared parser for `platform/neo4j/schema.cypher`.
|
|
3
|
+
*
|
|
4
|
+
* Two consumers today:
|
|
5
|
+
* 1. The admin SCHEMA prompt block (`platform/ui/app/lib/admin-schema-block.ts`,
|
|
6
|
+
* Task 703) renders labels declared in schema.cypher into the agent's
|
|
7
|
+
* system prompt.
|
|
8
|
+
* 2. The memory-plugin schema validator (Task 736) treats schema.cypher
|
|
9
|
+
* declarations as the "promise" half of the recognised-label set —
|
|
10
|
+
* labels that exist as constraints/indexes but may not yet have a node
|
|
11
|
+
* in the graph (fresh-install bootstrap).
|
|
12
|
+
*
|
|
13
|
+
* The previous home was admin-schema-block.ts; that owner was wrong as
|
|
14
|
+
* soon as a second consumer arrived. Lifting here keeps a single source of
|
|
15
|
+
* truth and removes the cross-package shape coupling.
|
|
16
|
+
*
|
|
17
|
+
* Also exports a compact `levenshtein` for "did you mean?" suggestions on
|
|
18
|
+
* unknown labels — used by the memory-plugin validator. The graph-mcp
|
|
19
|
+
* cypher validator has its own copy in schema-cache.ts (private to the
|
|
20
|
+
* stale-miss heuristic); the duplication is intentional, since this
|
|
21
|
+
* exported one targets human-readable error UX, not refresh debouncing.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Extract every Neo4j label declared in a `schema.cypher` file by scanning
|
|
25
|
+
* `FOR (alias:Label)` constraint and index forms. Returns a sorted, de-duped
|
|
26
|
+
* list. Pure function — no I/O.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseLabelsFromSchemaCypher(schemaCypher: string): string[];
|
|
29
|
+
/**
|
|
30
|
+
* Standard iterative-DP Levenshtein. Used to suggest a near-match when an
|
|
31
|
+
* agent submits an unknown label — small alphabets, short strings, runs in
|
|
32
|
+
* microseconds. Identical algorithm to the private copy in schema-cache.ts;
|
|
33
|
+
* exported here so callers outside graph-mcp don't have to roll their own.
|
|
34
|
+
*/
|
|
35
|
+
export declare function levenshtein(a: string, b: string): number;
|
|
36
|
+
/**
|
|
37
|
+
* Find the closest label in `candidates` to `unknown` by edit distance.
|
|
38
|
+
* Returns null when the closest match is further than `maxDistance` (default
|
|
39
|
+
* 3) — beyond that a suggestion is more confusing than helpful.
|
|
40
|
+
*/
|
|
41
|
+
export declare function nearestLabel(unknown: string, candidates: Iterable<string>, maxDistance?: number): string | null;
|
|
42
|
+
//# sourceMappingURL=schema-cypher-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-cypher-parser.d.ts","sourceRoot":"","sources":["../src/schema-cypher-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAIH;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE,CAM1E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBxD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC5B,WAAW,SAAI,GACd,MAAM,GAAG,IAAI,CAYf"}
|