memhouse 0.18.12 → 0.20.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/AGENTS.md +22 -151
- package/CHANGELOG.md +263 -29
- package/LICENSE +202 -22
- package/LICENSES/agentlytics-MIT.txt +21 -0
- package/LICENSES/memhouse-MIT-until-0.18.11.txt +22 -0
- package/LICENSES/models.dev-MIT.txt +22 -0
- package/NOTICE +28 -0
- package/README.md +42 -20
- package/SECURITY.md +21 -8
- package/bin/memhouse.js +2223 -311
- package/docs/README.md +11 -9
- package/docs/guides/admin-password.md +37 -10
- package/docs/guides/configuration.md +13 -6
- package/docs/guides/daemons.md +64 -3
- package/docs/guides/import-claude-ai.md +204 -0
- package/docs/guides/install.md +11 -6
- package/docs/guides/invite.md +21 -7
- package/docs/guides/operate.md +114 -7
- package/docs/guides/search-and-resume.md +72 -3
- package/docs/guides/share.md +91 -52
- package/docs/guides/storage.md +44 -4
- package/docs/guides/troubleshooting.md +121 -14
- package/docs/guides/uninstall.md +4 -3
- package/docs/guides/update.md +16 -0
- package/docs/guides/upgrade-to-0.20.md +188 -0
- package/docs/releases/README.md +2 -1
- package/docs/releases/v0.18.10.md +5 -5
- package/docs/releases/v0.18.11.md +3 -3
- package/docs/releases/v0.20.0.md +627 -0
- package/docs/releases/v0.20.1.md +114 -0
- package/docs/tutorials/01-first-house.md +10 -7
- package/docs/tutorials/02-join-a-house.md +24 -15
- package/docs/tutorials/03-share-your-memory.md +40 -31
- package/editors/antigravity.js +1 -80
- package/editors/base.js +463 -9
- package/editors/bridge.js +119 -0
- package/editors/claude.js +273 -105
- package/editors/codex.js +278 -52
- package/editors/copilot.js +1 -70
- package/editors/cursor.js +1 -75
- package/editors/derive-fingerprints.json +18 -0
- package/editors/harness-paths.js +69 -0
- package/editors/index.js +1 -20
- package/editors/normalise/agy.js +910 -0
- package/editors/normalise/copilot-cli.js +321 -0
- package/editors/normalise/cursor-agent.js +852 -0
- package/editors/normalise/cursor.js +474 -0
- package/editors/normalise/devin.js +575 -0
- package/editors/normalise/gemini-cli.js +602 -0
- package/editors/normalise/goose.js +673 -0
- package/editors/normalise/opencode.js +633 -0
- package/editors/normalise/vscode.js +549 -0
- package/editors/normalise/zed.js +473 -0
- package/editors/opencode-v4.js +119 -0
- package/editors/scope.js +14 -3
- package/editors/sessions-log.js +349 -0
- package/editors/sqlite.js +28 -1
- package/editors/vscode.js +1 -72
- package/editors/windsurf.js +1 -143
- package/examples/01-local-one-machine/README.md +24 -19
- package/examples/01-local-one-machine/run.sh +1 -1
- package/examples/02-existing-clickhouse/README.md +30 -22
- package/examples/02-existing-clickhouse/run.sh +5 -4
- package/examples/03-team-house/README.md +44 -37
- package/examples/03-team-house/run.sh +35 -18
- package/examples/04-self-hosted-team/README.md +29 -14
- package/examples/04-self-hosted-team/compose.yml +5 -2
- package/examples/04-self-hosted-team/smoke.sh +26 -12
- package/examples/README.md +8 -4
- package/examples/fixtures/README.md +4 -1
- package/examples/fixtures/make-sessions.js +8 -5
- package/memhouse/admin-secret.js +89 -24
- package/memhouse/argv-secrets.js +64 -0
- package/memhouse/channel.js +49 -9
- package/memhouse/codex-plugin.js +194 -0
- package/memhouse/delivery/AGENT-INSTALL-PROMPT.md +2 -1
- package/memhouse/delivery/AGENT-INSTALL.md +22 -11
- package/memhouse/delivery/INVITATION.md.tpl +44 -45
- package/memhouse/delivery/PROMPT.md +53 -5
- package/memhouse/delivery/UPGRADE.md.tpl +1 -1
- package/memhouse/delivery/codex/AGENTS-BLOCK.md +11 -0
- package/memhouse/delivery/codex/SKILL.md +50 -0
- package/memhouse/delivery/kernel-install.md +17 -9
- package/memhouse/delivery/plugin/.claude-plugin/plugin.json +2 -2
- package/memhouse/delivery/plugin/reference/HOUSE.md +168 -41
- package/memhouse/delivery/plugin/skills/access/SKILL.md +13 -5
- package/memhouse/delivery/plugin/skills/admin/SKILL.md +14 -6
- package/memhouse/delivery/plugin/skills/house/SKILL.md +25 -8
- package/memhouse/delivery/plugin/skills/recall/SKILL.md +66 -8
- package/memhouse/delivery/plugin/skills/sql/SKILL.md +66 -7
- package/memhouse/deploy.js +1 -1
- package/memhouse/flags.js +35 -18
- package/memhouse/help.js +37 -0
- package/memhouse/host.js +82 -2
- package/memhouse/house/HOUSE.md +2 -2
- package/memhouse/house/convert-profile.js +638 -0
- package/memhouse/house/convert-v3.js +742 -0
- package/memhouse/house/doctor-v3.js +324 -0
- package/memhouse/house/host-alias.js +85 -0
- package/memhouse/house/house.js +3 -3
- package/memhouse/house/migrate.js +2 -2
- package/memhouse/house/profile-dedupe.js +183 -0
- package/memhouse/house/profile-edges.js +554 -0
- package/memhouse/house/profile-house.js +430 -0
- package/memhouse/house/profile-rollup.js +190 -0
- package/memhouse/house/q-cli.js +863 -0
- package/memhouse/house/queries-v3.js +1013 -0
- package/memhouse/house/reconcile-v3.js +192 -0
- package/memhouse/house/records-to-profile.js +174 -0
- package/memhouse/house/records-to-rows.js +169 -0
- package/memhouse/house/relocate.js +2 -1
- package/memhouse/house/schema-choice.js +121 -0
- package/memhouse/house/schema-profile.js +290 -0
- package/memhouse/house/schema-profile.sql.tpl +398 -0
- package/memhouse/house/schema-v3.js +267 -0
- package/memhouse/house/schema-v3.sql.tpl +213 -0
- package/memhouse/import/claude-ai.js +420 -0
- package/memhouse/import/import.js +372 -0
- package/memhouse/import/pyjson.js +206 -0
- package/memhouse/import/records.js +64 -0
- package/memhouse/import/writer-profile.js +192 -0
- package/memhouse/import/writer-v3.js +240 -0
- package/memhouse/import/zip.js +112 -0
- package/memhouse/invitation.js +102 -0
- package/memhouse/invitefile.js +5 -2
- package/memhouse/plugin-copies.js +176 -0
- package/memhouse/provision.js +7 -2
- package/memhouse/release-hint.js +24 -0
- package/memhouse/resume.js +4 -4
- package/memhouse/server/api-v3.js +632 -0
- package/memhouse/server/dashboard-auth.js +181 -0
- package/memhouse/server/dashboard-port.js +91 -0
- package/memhouse/server/queries.js +30 -0
- package/memhouse/server/server.js +118 -20
- package/memhouse/service.js +1 -1
- package/memhouse/share.js +687 -1
- package/memhouse/shipper/adapter-capture.js +756 -0
- package/memhouse/shipper/capture.js +2571 -0
- package/memhouse/shipper/dedupe-profile.js +237 -0
- package/memhouse/shipper/dedupe-v3.js +1027 -0
- package/memhouse/shipper/forks.js +233 -0
- package/memhouse/shipper/log-adapter.js +219 -0
- package/memhouse/shipper/maintenance.js +49 -0
- package/memhouse/shipper/normalise-run.js +148 -0
- package/memhouse/shipper/passlog.js +68 -5
- package/memhouse/shipper/profile-capture.js +903 -0
- package/memhouse/shipper/ship.js +91 -8
- package/memhouse/shipper/snapshots.js +193 -0
- package/memhouse/shipper/source-roots.js +111 -0
- package/memhouse/version-notice.js +66 -0
- package/package.json +14 -7
- package/public/THIRD_PARTY_NOTICES.txt +2 -2
- package/public/assets/index-CcNnhfM3.js +78 -0
- package/public/index.html +1 -1
- package/ui/package-lock.json +56 -37
- package/ui/package.json +1 -0
- package/ui/src/App.jsx +29 -14
- package/ui/src/components/ChatSidebar.jsx +69 -12
- package/ui/src/lib/api.js +76 -0
- package/ui/src/lib/dashboard-token.js +41 -0
- package/ui/src/main.jsx +22 -5
- package/ui/src/pages/Dashboard.jsx +22 -36
- package/ui/third-party-notices.js +119 -0
- package/ui/vite.config.js +5 -1
- package/docs/releases/v0.18.12.md +0 -59
- package/memhouse/COMPETITION.md +0 -221
- package/memhouse/DESIGN.md +0 -222
- package/memhouse/RESUME-AND-UPDATE.md +0 -182
- package/public/assets/index-CnWzrjF3.js +0 -78
package/memhouse/invitefile.js
CHANGED
|
@@ -13,13 +13,16 @@ const envfile = require('./envfile');
|
|
|
13
13
|
|
|
14
14
|
const REQUIRED = ['MEMHOUSE_URL', 'MEMHOUSE_USER', 'MEMHOUSE_PASSWORD', 'MEMHOUSE_DB'];
|
|
15
15
|
|
|
16
|
+
/** The names bare `memhouse` recognises as an invite file: `invite-<name>.env`. */
|
|
17
|
+
function isInviteFileName(n) { return /^invite-[A-Za-z0-9_]+\.env$/.test(String(n)); }
|
|
18
|
+
|
|
16
19
|
/** `invite-<name>.env` files in the given directories, first directory first. */
|
|
17
20
|
function findInvites(dirs) {
|
|
18
21
|
const out = [];
|
|
19
22
|
for (const dir of dirs) {
|
|
20
23
|
let names = [];
|
|
21
24
|
try { names = fs.readdirSync(dir); } catch { continue; }
|
|
22
|
-
for (const n of names.sort()) if (
|
|
25
|
+
for (const n of names.sort()) if (isInviteFileName(n)) out.push({ file: path.join(dir, n), dir });
|
|
23
26
|
}
|
|
24
27
|
return out;
|
|
25
28
|
}
|
|
@@ -35,4 +38,4 @@ function describeInvite(text) {
|
|
|
35
38
|
};
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
module.exports = { findInvites, describeInvite, REQUIRED };
|
|
41
|
+
module.exports = { isInviteFileName, findInvites, describeInvite, REQUIRED };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// The /mem plugin's installed copies, and keeping them in step with the memhouse that runs them.
|
|
2
|
+
//
|
|
3
|
+
// `plugins install claude` copies memhouse/delivery/plugin into a Claude Code config dir and
|
|
4
|
+
// binds the dir to this instance (memhouse/binding.js). Only `memhouse update` refreshed those
|
|
5
|
+
// copies — and an upgrade with `npm install -g` never runs it, nor can update run behind a
|
|
6
|
+
// registry mirror. A 0.18 copy then kept teaching the 0.18 rooms to agents on a schema-3 house,
|
|
7
|
+
// where every documented query fails. So the copies are refreshed on the new version's first
|
|
8
|
+
// run instead: any CLI call, or the shipper's start. A stamp in the home records the version
|
|
9
|
+
// that last checked, so every later call costs one small read.
|
|
10
|
+
//
|
|
11
|
+
// Only copies BOUND TO THIS HOME are touched: a machine can run two instances on two versions,
|
|
12
|
+
// and another instance's copy belongs to that instance's binary. A copy is never downgraded.
|
|
13
|
+
// The Codex copy (memhouse/codex-plugin.js) is refreshed the same way, and only where
|
|
14
|
+
// `plugins install codex` put it: its skill carries memhouse's ownership file and this home.
|
|
15
|
+
// From a git checkout nothing happens unless MEMHOUSE_REFRESH_SKILLS=1: a checkout run with a
|
|
16
|
+
// real MEMHOUSE_HOME would otherwise copy work-in-progress skills over the installed ones.
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const PLUGIN_MARK = path.join('skills', 'mem', '.claude-plugin', 'plugin.json');
|
|
23
|
+
const STAMP = 'skills-checked';
|
|
24
|
+
const isPluginInstalled = (dir) => fs.existsSync(path.join(dir, PLUGIN_MARK));
|
|
25
|
+
|
|
26
|
+
/** -1, 0, 1 for x.y.z versions; a prerelease sorts before its release. Unparseable -> null. */
|
|
27
|
+
function compareVersions(a, b) {
|
|
28
|
+
const p = (v) => { const m = /^(\d+)\.(\d+)\.(\d+)(-.+)?$/.exec(String(v || '').trim()); return m ? [+m[1], +m[2], +m[3], m[4] ? 0 : 1] : null; };
|
|
29
|
+
const x = p(a); const y = p(b);
|
|
30
|
+
if (!x || !y) return null;
|
|
31
|
+
for (let i = 0; i < 4; i++) if (x[i] !== y[i]) return x[i] < y[i] ? -1 : 1;
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Every Claude Code instance on this machine, not just the default one: CLAUDE_CONFIG_DIR, the
|
|
36
|
+
// stock ~/.claude, and the playbook installs the adapter discovers (editors/claude.js).
|
|
37
|
+
function claudeTargets() {
|
|
38
|
+
const seen = new Map(); // realpath -> { dir, why }
|
|
39
|
+
const add = (dir, why) => {
|
|
40
|
+
if (!dir) return;
|
|
41
|
+
let isDir = false;
|
|
42
|
+
try { isDir = fs.statSync(dir).isDirectory(); } catch { /* missing */ }
|
|
43
|
+
if (!isDir) return;
|
|
44
|
+
let rp; try { rp = fs.realpathSync(dir); } catch { rp = dir; }
|
|
45
|
+
if (!seen.has(rp)) seen.set(rp, { dir, why });
|
|
46
|
+
};
|
|
47
|
+
// The instance the caller is running under comes first, and is NOT subject to the
|
|
48
|
+
// adapter's "has this been used?" test — a fresh config dir has no projects/ yet, and it
|
|
49
|
+
// is exactly where the skills are wanted.
|
|
50
|
+
add(process.env.CLAUDE_CONFIG_DIR, 'CLAUDE_CONFIG_DIR');
|
|
51
|
+
add(path.join(os.homedir(), '.claude'), 'default');
|
|
52
|
+
try {
|
|
53
|
+
const { discoverClaudeRoots } = require(path.join(__dirname, '..', 'editors', 'claude'));
|
|
54
|
+
for (const r of discoverClaudeRoots()) add(r, 'playbook');
|
|
55
|
+
} catch { /* adapter unavailable — the two above still stand */ }
|
|
56
|
+
return [...seen.values()];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Replace the plugin copy in `dir` with `deliveryDir/plugin`, stamped with `version`.
|
|
61
|
+
* @param {(line: string) => void} [log]
|
|
62
|
+
* @returns {string} the copy's directory
|
|
63
|
+
*/
|
|
64
|
+
function installInto(dir, { deliveryDir, version, log = () => {} }) {
|
|
65
|
+
// The plugin was named `memhouse` until 0.10.0. A leftover copy under the old name
|
|
66
|
+
// would load BESIDE the new one — /memhouse:search and /mem:recall both resolving, one
|
|
67
|
+
// of them stale forever. Remove it only when it is provably OURS (it carries our
|
|
68
|
+
// plugin.json); a directory someone else named `memhouse` is not ours to delete.
|
|
69
|
+
const legacy = path.join(dir, 'skills', 'memhouse');
|
|
70
|
+
const legacyManifest = path.join(legacy, '.claude-plugin', 'plugin.json');
|
|
71
|
+
if (fs.existsSync(legacyManifest)) {
|
|
72
|
+
let lname = null;
|
|
73
|
+
try { lname = JSON.parse(fs.readFileSync(legacyManifest, 'utf-8')).name; } catch { /* unreadable */ }
|
|
74
|
+
if (lname === 'memhouse' || lname === 'mem') {
|
|
75
|
+
fs.rmSync(legacy, { recursive: true });
|
|
76
|
+
log(`removed the pre-0.10 plugin at ${legacy} (renamed to 'mem')`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const dst = path.join(dir, 'skills', 'mem');
|
|
80
|
+
// REPLACE, not overlay. cpSync over an existing install refreshes the skills and
|
|
81
|
+
// leaves anything else standing — a machine that once had a build with extra skills
|
|
82
|
+
// kept offering /mem:replay and /mem:house forever, stale, beside the real ones.
|
|
83
|
+
// Ownership means OUR MANIFEST, checked by name — "any plugin.json" would have deleted
|
|
84
|
+
// an unrelated plugin that happened to pick the same directory name.
|
|
85
|
+
const dstManifest = path.join(dst, '.claude-plugin', 'plugin.json');
|
|
86
|
+
if (fs.existsSync(dstManifest)) {
|
|
87
|
+
let name = null;
|
|
88
|
+
try { name = JSON.parse(fs.readFileSync(dstManifest, 'utf-8')).name; } catch { /* unreadable */ }
|
|
89
|
+
if (name === 'mem' || name === 'memhouse') fs.rmSync(dst, { recursive: true });
|
|
90
|
+
else throw new Error(`skills/mem in ${dir} belongs to plugin '${name || '(unreadable manifest)'}' — refusing to replace it`);
|
|
91
|
+
}
|
|
92
|
+
fs.mkdirSync(dst, { recursive: true });
|
|
93
|
+
fs.cpSync(path.join(deliveryDir, 'plugin'), dst, { recursive: true });
|
|
94
|
+
// Stamp the manifest with THIS memhouse's version. The source plugin.json carries a
|
|
95
|
+
// FROZEN number — it sat at 0.11.0 through several releases, so every install
|
|
96
|
+
// advertised the wrong version (`memhouse --version` said one thing, the plugin
|
|
97
|
+
// another). The package version is the single truth; write it into the copy.
|
|
98
|
+
try {
|
|
99
|
+
const m = JSON.parse(fs.readFileSync(dstManifest, 'utf-8'));
|
|
100
|
+
if (m.version !== version) {
|
|
101
|
+
m.version = version;
|
|
102
|
+
fs.writeFileSync(dstManifest, `${JSON.stringify(m, null, 2)}\n`);
|
|
103
|
+
}
|
|
104
|
+
} catch { /* manifest unreadable — leave the copied one rather than guess */ }
|
|
105
|
+
return dst;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const real = (p) => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|
|
109
|
+
|
|
110
|
+
/** The installed copy's version, or null. */
|
|
111
|
+
function copyVersion(dir) {
|
|
112
|
+
try { return JSON.parse(fs.readFileSync(path.join(dir, PLUGIN_MARK), 'utf-8')).version || null; } catch { return null; }
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** The dir's binding to a memhouse home, or null (memhouse/binding.js). */
|
|
116
|
+
function boundHome(dir) {
|
|
117
|
+
const { boundTo, readSettings } = require('./binding');
|
|
118
|
+
const b = boundTo(readSettings(dir));
|
|
119
|
+
return b && b.home ? b.home : null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Copies bound to `home` and older than `version`.
|
|
124
|
+
* @returns {{dir: string, have: string|null}[]}
|
|
125
|
+
*/
|
|
126
|
+
function staleCopies({ home, version, targets = claudeTargets() }) {
|
|
127
|
+
const out = [];
|
|
128
|
+
for (const t of targets) {
|
|
129
|
+
if (!isPluginInstalled(t.dir)) continue;
|
|
130
|
+
const bound = boundHome(t.dir);
|
|
131
|
+
if (!bound || real(bound) !== real(home)) continue;
|
|
132
|
+
const have = copyVersion(t.dir);
|
|
133
|
+
const c = compareVersions(have, version);
|
|
134
|
+
if (have === null || c === -1) out.push({ dir: t.dir, have });
|
|
135
|
+
}
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Is this memhouse running from a git checkout rather than an installed package? */
|
|
140
|
+
function fromCheckout(repoRoot) {
|
|
141
|
+
return fs.existsSync(path.join(repoRoot, '.git'));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* On a new version's first run: refresh the stale copies bound to this home. Cheap when the
|
|
146
|
+
* stamp already names this version. Never throws; returns what it refreshed.
|
|
147
|
+
* @returns {{refreshed: {dir: string, have: string|null}[], skipped: string|null, errors: string[]}}
|
|
148
|
+
*/
|
|
149
|
+
function refreshIfStale({ home, version, deliveryDir, repoRoot, env = process.env, targets = null, codexHome = null, log = () => {} }) {
|
|
150
|
+
const res = { refreshed: [], skipped: null, errors: [] };
|
|
151
|
+
try {
|
|
152
|
+
if (fromCheckout(repoRoot) && env.MEMHOUSE_REFRESH_SKILLS !== '1') { res.skipped = 'checkout'; return res; }
|
|
153
|
+
const stamp = path.join(home, STAMP);
|
|
154
|
+
let seen = null; try { seen = fs.readFileSync(stamp, 'utf-8').trim(); } catch { /* first run */ }
|
|
155
|
+
if (seen === version) { res.skipped = 'stamped'; return res; }
|
|
156
|
+
for (const s of staleCopies({ home, version, targets: targets || claudeTargets() })) {
|
|
157
|
+
try { installInto(s.dir, { deliveryDir, version }); res.refreshed.push(s); log(`/mem skills in ${s.dir} refreshed for ${version}${s.have ? ` (were ${s.have})` : ''} — reload them in Claude Code: /reload-plugins`); }
|
|
158
|
+
catch (e) { res.errors.push(`${s.dir}: ${String(e.message || e).split('\n')[0]}`); }
|
|
159
|
+
}
|
|
160
|
+
const cxp = require('./codex-plugin');
|
|
161
|
+
const cx = cxp.staleCopy({ codexHome: codexHome || cxp.codexHome(env), home, version, compareVersions });
|
|
162
|
+
if (cx) {
|
|
163
|
+
try {
|
|
164
|
+
// The block is rewritten only where it still stands: one taken out by hand stays out.
|
|
165
|
+
cxp.install({ codexHome: cx.codexHome, home: cx.home, bin: cx.bin, version, deliveryDir, onlyWhereInstalled: true });
|
|
166
|
+
res.refreshed.push({ dir: cx.codexHome, have: cx.have, codex: true });
|
|
167
|
+
log(`the memhouse skill and block in ${cx.codexHome} refreshed for ${version}${cx.have ? ` (were ${cx.have})` : ''} — they load in the next Codex session`);
|
|
168
|
+
} catch (e) { res.errors.push(`${cx.codexHome}: ${String(e.message || e).split('\n')[0]}`); }
|
|
169
|
+
}
|
|
170
|
+
// Stamp only when every copy is current, so a failure is tried again on the next run.
|
|
171
|
+
if (!res.errors.length && fs.existsSync(home)) { try { fs.writeFileSync(stamp, `${version}\n`); } catch { /* home not writable: check again next run */ } }
|
|
172
|
+
} catch (e) { res.errors.push(String(e.message || e).split('\n')[0]); }
|
|
173
|
+
return res;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
module.exports = { PLUGIN_MARK, STAMP, isPluginInstalled, compareVersions, claudeTargets, installInto, copyVersion, boundHome, staleCopies, fromCheckout, refreshIfStale };
|
package/memhouse/provision.js
CHANGED
|
@@ -27,12 +27,17 @@ const { MEMBER_PIN, assertUsableName, roomPattern } = require('./house/house');
|
|
|
27
27
|
* SELECT, INSERT the shipper writes, everything else reads
|
|
28
28
|
* ALTER, OPTIMIZE ensureSchema adds columns in place; doctor compacts
|
|
29
29
|
* CREATE TABLE, DROP TABLE their own rooms on first ship; a rebuild on migrate
|
|
30
|
-
* ROW POLICY
|
|
30
|
+
* CREATE/ALTER/DROP ROW POLICY `share --only` scopes a grantee to a project or a session
|
|
31
|
+
*
|
|
32
|
+
* Not SHOW ROW POLICIES: held on any table, it makes all of system.row_policies readable (a
|
|
33
|
+
* SHOW privilege opens its system table, and that one is not filtered per table), so a member
|
|
34
|
+
* would list every other member's scoped shares. A share does not need it: memhouse keeps what it
|
|
35
|
+
* shared in the member's own meta, and the policies are created, replaced and dropped by name.
|
|
31
36
|
*/
|
|
32
37
|
const ROOM_PRIVILEGES = [
|
|
33
38
|
'SELECT', 'INSERT', 'ALTER', 'OPTIMIZE',
|
|
34
39
|
'CREATE TABLE', 'DROP TABLE',
|
|
35
|
-
'CREATE ROW POLICY', 'ALTER ROW POLICY', 'DROP ROW POLICY',
|
|
40
|
+
'CREATE ROW POLICY', 'ALTER ROW POLICY', 'DROP ROW POLICY',
|
|
36
41
|
];
|
|
37
42
|
|
|
38
43
|
function roomGrant(db, member) {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// What doctor says about a gap that a newer release might close (an unpriced model): whether a
|
|
2
|
+
// newer release exists on the channel this install follows, from the registry's dist-tags.
|
|
3
|
+
// Pure: the tags are given (bin/memhouse.js reads them once per doctor).
|
|
4
|
+
|
|
5
|
+
const chan = require('./channel');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {{version:string, pinned?:string|null, tags:Record<string,string>|null}} p
|
|
9
|
+
* @returns {{known:boolean, up:{target:string,channel:string}|null, channel:string|null}}
|
|
10
|
+
* `known` only when the registry answered and this install follows a tag on it.
|
|
11
|
+
*/
|
|
12
|
+
function releaseState({ version, pinned = null, tags = null }) {
|
|
13
|
+
if (!tags) return { known: false, up: null, channel: null };
|
|
14
|
+
const channel = chan.pickChannel({ version, pinned, tags }).channel;
|
|
15
|
+
return { known: !!channel, up: chan.behind({ version, pinned, tags }), channel };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function pricingHint(state, version) {
|
|
19
|
+
if (state.up) return `memhouse update installs ${state.up.target} from '${state.up.channel}', which may price it`;
|
|
20
|
+
if (state.known) return `this is the latest release on '${state.channel}' (${version}) and it has no price for it: its cost stays unknown until a release adds one`;
|
|
21
|
+
return 'prices ship with each release: a newer one may add them (memhouse update), and on the latest one their cost stays unknown until a release does';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { releaseState, pricingHint };
|
package/memhouse/resume.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Resume: hand the
|
|
1
|
+
// Resume: hand the user back into a session memhouse only ever read.
|
|
2
2
|
//
|
|
3
3
|
// Nothing here needs a schema change. A stored session already carries everything a
|
|
4
4
|
// resume needs: `session_id` is canonical `<source>:<native-id>` (ship.js:575) and
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// Two rules, both learned the hard way in this repo:
|
|
9
9
|
//
|
|
10
10
|
// 1. PRINT the command, never run it. A wrong cwd or a stale id does not fail — it
|
|
11
|
-
// starts a NEW session, and the transcript the
|
|
12
|
-
// tool reports success. The
|
|
11
|
+
// starts a NEW session, and the transcript the user wanted is still lost while the
|
|
12
|
+
// tool reports success. The user pastes it, sees what it does, and owns the result.
|
|
13
13
|
//
|
|
14
14
|
// 2. Never write a flag from memory. Round 10 exists because a field name was asserted
|
|
15
15
|
// from a failed search rather than read from the source. Every entry below was read
|
|
@@ -101,7 +101,7 @@ function shq(s) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
* Resolve a stored session row into something the
|
|
104
|
+
* Resolve a stored session row into something the user can paste.
|
|
105
105
|
*
|
|
106
106
|
* @param {{session_id: string, source?: string, folder?: string, origin?: string}} row
|
|
107
107
|
* @returns {{ok: boolean, source: string, nativeId: string|null, folder: string,
|