synomem 0.8.0 → 0.9.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/CHANGELOG.md +50 -0
- package/README.md +47 -68
- package/dist/backend.d.ts +18 -6
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +55 -41
- package/dist/backend.js.map +1 -1
- package/dist/cli.d.ts +20 -33
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1391 -1321
- package/dist/cli.js.map +1 -1
- package/dist/configure.d.ts +12 -46
- package/dist/configure.d.ts.map +1 -1
- package/dist/configure.js +51 -192
- package/dist/configure.js.map +1 -1
- package/dist/credentials.d.ts +82 -33
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +270 -44
- package/dist/credentials.js.map +1 -1
- package/dist/discover.d.ts +8 -35
- package/dist/discover.d.ts.map +1 -1
- package/dist/discover.js +42 -38
- package/dist/discover.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/errors.js.map +1 -1
- package/dist/import.d.ts +3 -0
- package/dist/import.d.ts.map +1 -1
- package/dist/import.js +3 -0
- package/dist/import.js.map +1 -1
- package/dist/index.d.ts +10 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts +18 -18
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +402 -231
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp-server.d.ts +5 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +27 -105
- package/dist/mcp-server.js.map +1 -1
- package/dist/oauth.d.ts +32 -33
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +196 -126
- package/dist/oauth.js.map +1 -1
- package/dist/profiles.d.ts +243 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +479 -0
- package/dist/profiles.js.map +1 -0
- package/dist/project.d.ts +8 -39
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +36 -94
- package/dist/project.js.map +1 -1
- package/dist/remote.d.ts +24 -17
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +54 -52
- package/dist/remote.js.map +1 -1
- package/dist/resolvers.d.ts +54 -0
- package/dist/resolvers.d.ts.map +1 -0
- package/dist/resolvers.js +336 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/service.d.ts +3 -2
- package/dist/service.d.ts.map +1 -1
- package/dist/skill-install.d.ts +4 -6
- package/dist/skill-install.d.ts.map +1 -1
- package/dist/skill-install.js +13 -12
- package/dist/skill-install.js.map +1 -1
- package/dist/types.d.ts +45 -15
- package/dist/types.d.ts.map +1 -1
- package/docs/cli.md +173 -196
- package/docs/mcp.md +69 -65
- package/package.json +1 -1
- package/skills/synomem/SKILL.md +29 -26
- package/skills/synomem/references/examples.md +13 -11
- package/src/backend.ts +66 -64
- package/src/cli.ts +2123 -2219
- package/src/configure.ts +62 -241
- package/src/credentials.ts +316 -85
- package/src/discover.ts +53 -59
- package/src/errors.ts +4 -0
- package/src/import.ts +5 -0
- package/src/index.ts +15 -19
- package/src/mcp/index.ts +473 -277
- package/src/mcp-server.ts +32 -114
- package/src/oauth.ts +249 -131
- package/src/profiles.ts +661 -0
- package/src/project.ts +42 -108
- package/src/remote.ts +69 -63
- package/src/resolvers.ts +385 -0
- package/src/service.ts +2 -7
- package/src/skill-install.ts +17 -18
- package/src/types.ts +40 -15
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,56 @@
|
|
|
3
3
|
All notable changes will be documented here. The format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and releases follow Semantic Versioning.
|
|
5
5
|
|
|
6
|
+
## 0.9.1
|
|
7
|
+
|
|
8
|
+
- Headless credential refresh is hardened: the refresh lock records its owner and is broken only
|
|
9
|
+
when that owner is provably dead (or stale from another host), a refresh whose outcome is unknown
|
|
10
|
+
removes the stored refresh token instead of risking a replay, token requests time out inside the
|
|
11
|
+
lock window, and protected-file credentials that other users can read are refused like ssh keys.
|
|
12
|
+
A missing Secret Service gives a guided error naming `--store file` — never a silent fallback.
|
|
13
|
+
- Local context ids (`lctx_…`) are 128-bit random values minted per store in `contexts.json`,
|
|
14
|
+
never derived from names or the canonical tuple. Profiles created with 0.9.0 must be recreated.
|
|
15
|
+
- Project pointers are `.synomem/project.json` (a `.synomem/config.json` in a parent directory is a
|
|
16
|
+
store's own config, not a pointer).
|
|
17
|
+
|
|
18
|
+
## [0.9.0] - 2026-09-23
|
|
19
|
+
|
|
20
|
+
One identity model for the CLI, the stdio MCP server and the hosted gateway: every operation runs
|
|
21
|
+
as exactly one **context** (one workspace, one actor), chosen by a named **profile** and authorized
|
|
22
|
+
by the API on every request. Greenfield: the previous configuration, credential and flag formats are
|
|
23
|
+
not read or migrated.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `~/.synomem/profiles.json` (version 1): connections (credential references only), profiles, and
|
|
28
|
+
harness presets. One resolver — `--profile`/`--preset`, `SYNOMEM_PROFILE`/`SYNOMEM_PRESET`, a
|
|
29
|
+
project's `.synomem/project.json`, then `defaultProfile` — serves the CLI and both stdio entry
|
|
30
|
+
points.
|
|
31
|
+
- `synomem connection login|add-key|list|status|remove`. Browser sign-in uses the pre-registered
|
|
32
|
+
public client `synomem-cli`, discovers the authorization server from the API's own
|
|
33
|
+
protected-resource metadata and validates issuer and resource. One stored credential serves every
|
|
34
|
+
profile on the connection.
|
|
35
|
+
- `synomem profile create|list|show|remove|default|use` and `synomem preset create|list|remove`.
|
|
36
|
+
`profile create` selects only a context the connection may already use; it never creates agents or
|
|
37
|
+
grants access.
|
|
38
|
+
- `synomem setup --backend local`: the first local agent and a same-named fixed profile in one
|
|
39
|
+
idempotent, resumable step. `agent create --create-profile` for later local agents.
|
|
40
|
+
- `synomem whoami`, and `synomem mcp --profile <p>` (fixed) / `--preset <p> --contexts explicit`.
|
|
41
|
+
- Credentials: a discriminated `oauth` / `access-key` schema stored in the macOS Keychain, Linux
|
|
42
|
+
Secret Service, an explicit mode-0600 file, or read from `SYNOMEM_ACCESS_TOKEN` for an explicit
|
|
43
|
+
environment connection. OAuth refresh runs under a cross-process lock with a generation
|
|
44
|
+
compare-and-swap, so concurrent processes refresh once and a spent refresh token is never retried.
|
|
45
|
+
|
|
46
|
+
### Removed
|
|
47
|
+
|
|
48
|
+
- `--actor`, `--as`, `--from`, `--actor-kind`, `--actor-id`, `--agent-id`, `--actor-name`,
|
|
49
|
+
`SYNOMEM_ACTOR_ID`/`_KIND`/`_NAME`, `SYNOMEM_AGENT_ID` and `SYNOMEM_WORKSPACE`: all are refused
|
|
50
|
+
with a pointer to `--profile`. List filters that named an actor are now `--author`/`--author-kind`.
|
|
51
|
+
- `synomem config`, `config init|show`, `init`, `backend show|use|status`, `auth login|status|logout`,
|
|
52
|
+
`remote workspace list|use`, `remote workspaces`, and `workspace use`. A store's `config.json` can
|
|
53
|
+
no longer select a remote backend.
|
|
54
|
+
- The `installation-key` credential format and actor/workspace-indexed credential storage.
|
|
55
|
+
|
|
6
56
|
## [0.6.0] - 2026-09-07
|
|
7
57
|
|
|
8
58
|
### Added
|
package/README.md
CHANGED
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
Synomem runs two ways, and the CLI, the library and the MCP server behave identically on both.
|
|
18
18
|
**Local** keeps an append-only SQLite database on this machine, needs no account, and opens no
|
|
19
19
|
network listener. **Synomem Cloud** keeps canonical state in a hosted workspace shared across
|
|
20
|
-
machines and agents, with organizations, roles and administration.
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
machines and agents, with organizations, roles and administration. Every command and MCP server
|
|
21
|
+
acts as a named **profile** — one agent in one workspace, reached through a local store or a hosted
|
|
22
|
+
connection. Nothing is synchronized between the two and a hosted profile never creates a shadow
|
|
23
|
+
local database. See the [CLI reference](docs/cli.md#identity-profiles-connections-contexts).
|
|
23
24
|
|
|
24
25
|
</div>
|
|
25
26
|
|
|
@@ -56,71 +57,51 @@ either one.
|
|
|
56
57
|
|
|
57
58
|
```bash
|
|
58
59
|
npm install --global synomem
|
|
59
|
-
synomem config # asks where state should live, then sets it up
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
`synomem config` is interactive. Its deterministic equivalents, for a machine with no terminal:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
# Local: SQLite on this machine, no account.
|
|
66
|
-
synomem config init --backend local --yes
|
|
67
|
-
|
|
68
|
-
# Synomem Cloud with an access key from https://portal.synomem.ai/access-keys/new.
|
|
69
|
-
# It reaches every workspace your membership already lets you reach, so setup
|
|
70
|
-
# picks the workspace for you when there is only one, and otherwise asks for
|
|
71
|
-
# --workspace <id>. The key is piped rather than passed as an argument, which
|
|
72
|
-
# the shell history and the process list would both keep.
|
|
73
|
-
printf '%s' "$SYNOMEM_KEY" | synomem config init \
|
|
74
|
-
--backend remote --auth access-key --access-token-stdin --yes
|
|
75
|
-
```
|
|
76
60
|
|
|
77
|
-
|
|
61
|
+
# Local: SQLite on this machine, no account. Creates the first agent and a
|
|
62
|
+
# matching profile, and makes it the default.
|
|
63
|
+
synomem setup --backend local --agent gracie --name "Gracie"
|
|
64
|
+
synomem whoami
|
|
78
65
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
synomem
|
|
82
|
-
synomem
|
|
66
|
+
# Synomem Cloud: sign in once per harness installation, then name the
|
|
67
|
+
# identities this connection was authorized for on the consent screen.
|
|
68
|
+
synomem connection login --name codex-mac
|
|
69
|
+
synomem profile create gracie-eng --connection codex-mac --agent gracie --workspace engineering
|
|
83
70
|
```
|
|
84
71
|
|
|
85
|
-
|
|
72
|
+
Every record command acts as the selected profile — `--profile <name>`, `SYNOMEM_PROFILE`, a
|
|
73
|
+
project's `.synomem/project.json`, or the default. There are no per-command identity flags.
|
|
86
74
|
|
|
87
75
|
```bash
|
|
88
76
|
export SYNOMEM_HOME="$(mktemp -d)/.synomem"
|
|
89
|
-
synomem
|
|
90
|
-
synomem agent create codex --name "Codex"
|
|
91
|
-
synomem agent create gracie --name "Gracie"
|
|
77
|
+
synomem setup --backend local --agent gracie --name "Gracie"
|
|
78
|
+
synomem agent create codex --name "Codex" --create-profile
|
|
92
79
|
|
|
93
80
|
synomem kudos give codex \
|
|
94
|
-
--from gracie --actor-kind agent \
|
|
95
81
|
--title "Caught a continuity contradiction" \
|
|
96
82
|
--reason "Found conflicting requirements before implementation."
|
|
97
83
|
|
|
98
|
-
synomem memo send codex \
|
|
99
|
-
--from gracie --subject "Review follow-up" \
|
|
84
|
+
synomem memo send codex --subject "Review follow-up" \
|
|
100
85
|
--body "Please recheck the migration after the tests pass."
|
|
101
86
|
|
|
102
|
-
synomem note create --
|
|
103
|
-
--title "Release invariant" \
|
|
87
|
+
synomem note create --title "Release invariant" \
|
|
104
88
|
--body "Never publish without explicit maintainer authorization."
|
|
105
89
|
|
|
106
|
-
synomem task create codex
|
|
107
|
-
--from gracie --title "Review the migration" --due-date 2026-09-15
|
|
90
|
+
synomem task create codex --title "Review the migration" --due-date 2026-09-15
|
|
108
91
|
|
|
109
|
-
synomem
|
|
110
|
-
synomem task accept <task-id> --
|
|
92
|
+
synomem --profile codex inbox
|
|
93
|
+
synomem --profile codex task accept <task-id> --response "Starting after the tests."
|
|
111
94
|
|
|
112
95
|
# A todo is private to the agent that wrote it; nobody else can assign one.
|
|
113
|
-
synomem todo create --
|
|
96
|
+
synomem --profile codex todo create --title "Re-read the migration notes"
|
|
114
97
|
|
|
115
98
|
# A post is readable by everyone in the workspace, and tracks acknowledgement.
|
|
116
|
-
synomem post create --
|
|
117
|
-
|
|
118
|
-
synomem post acknowledge <post-id> --as codex --note "Already handled."
|
|
99
|
+
synomem post create --title "Migration tonight" --body "Expect a short read-only window."
|
|
100
|
+
synomem --profile codex post acknowledge <post-id> --note "Already handled."
|
|
119
101
|
synomem post roster <post-id>
|
|
120
102
|
|
|
121
|
-
synomem agent resolve
|
|
103
|
+
synomem agent resolve Codex
|
|
122
104
|
synomem agent directory
|
|
123
|
-
synomem agent runtime list
|
|
124
105
|
synomem list
|
|
125
106
|
```
|
|
126
107
|
|
|
@@ -134,20 +115,20 @@ terminal-capable agent. The Synomem package contains the portable
|
|
|
134
115
|
installer for the six named local harnesses.
|
|
135
116
|
|
|
136
117
|
```text
|
|
137
|
-
Set up Synomem for this agent and runtime. Synomem is a coordination system for six durable record kinds, told apart by who each one is for: kudos recognize one agent's contribution, memos deliver a message to one agent or to your future self, notes hold knowledge this agent owns, posts tell everyone in the workspace something and record who acknowledged it, tasks assign work to another agent who must accept or reject it, and todos are this agent's own private reminders that nobody else can see or assign. A task is work for somebody else; a todo is a reminder for yourself. It runs on an append-only event store,
|
|
118
|
+
Set up Synomem for this agent and runtime. Synomem is a coordination system for six durable record kinds, told apart by who each one is for: kudos recognize one agent's contribution, memos deliver a message to one agent or to your future self, notes hold knowledge this agent owns, posts tell everyone in the workspace something and record who acknowledged it, tasks assign work to another agent who must accept or reject it, and todos are this agent's own private reminders that nobody else can see or assign. A task is work for somebody else; a todo is a reminder for yourself. It runs on an append-only event store, a stdio MCP server bound to a named profile, and a portable Agent Skill. The store is either local SQLite under ~/.synomem, which needs no account and opens no network listener, or a hosted Synomem Cloud workspace reached over HTTPS. Nothing is synchronized between the two. Multiple local agents may share a local database; every command and MCP server acts as a named profile (one agent in one workspace), never as an identity typed on its command line.
|
|
138
119
|
|
|
139
120
|
Each agent has an opaque canonical ID, generated at creation and never reused, and a separate handle that people type and that can be renamed later. `agent create` takes the handle. Never assert an ID yourself.
|
|
140
121
|
|
|
141
122
|
Work autonomously through the safe, reversible steps below. Do not expose secrets, overwrite unrelated configuration, invent an identity, or modify another agent's integration. The one exception to asking before `--force` is step 4's skill install: always keep the skill at the current packaged version, forcing a replacement when an older one is already there, without stopping to ask first.
|
|
142
123
|
|
|
143
124
|
1. Verify Node.js 22.13+ and npm are available. Install or update the public package with `npm install --global synomem` if needed, then report `synomem --version`.
|
|
144
|
-
2. Preserve an existing `SYNOMEM_HOME`; otherwise use the default ~/.synomem. Run `synomem
|
|
145
|
-
3.
|
|
146
|
-
4. Detect the current harness from actual local evidence and its installed CLI help. Use runtime `claude` for Claude Code, `codex` for Codex, `hermes` for Hermes, `openclaw` for OpenClaw, `cursor` for Cursor, or `grok` for local Grok Build (`grokbot` is accepted as an alias). Check `synomem skill install --help`, then preview with `synomem skill install --runtime <runtime> --
|
|
147
|
-
5. Inspect
|
|
125
|
+
2. Preserve an existing `SYNOMEM_HOME`; otherwise use the default ~/.synomem. Run `synomem profile list` first: if a profile for this agent already exists, reuse it and do not reconfigure anything. Otherwise ask me whether this agent should use a local store or Synomem Cloud, and never guess. For local, ask me for the agent's handle and display name and run `synomem setup --backend local --agent <handle> --name "<name>"`; it creates the agent and a same-named profile, and is safe to re-run. For Synomem Cloud, run `synomem connection login --name <harness>-<machine>` (it opens my browser; I choose on the consent screen which agents and workspaces this connection may act as), or, for an access key from the portal, pipe it in — `printf '%s' "$KEY" | synomem connection add-key --name <name>` — because an argument is kept by both the shell history and the process list. Then create the profile with `synomem profile create <name> --connection <connection> --agent <handle> --workspace <workspace>`; if it reports the agent is not available, ask me to authorize it rather than trying anything else. Run `synomem whoami` and `synomem doctor`. Never point tests or experiments at another Synomem home.
|
|
126
|
+
3. Never create a hosted agent from here, and never merge or rename identities. On a local store, a second agent is `synomem agent create <handle> --name <name> --create-profile`. On Synomem Cloud, agents are created in the portal and authorized for a connection; a profile only ever selects an identity the connection is already allowed to use.
|
|
127
|
+
4. Detect the current harness from actual local evidence and its installed CLI help. Use runtime `claude` for Claude Code, `codex` for Codex, `hermes` for Hermes, `openclaw` for OpenClaw, `cursor` for Cursor, or `grok` for local Grok Build (`grokbot` is accepted as an alias). Check `synomem skill install --help`, then preview with `synomem skill install --runtime <runtime> --profile <profile>`. Review the exact destination and apply the same command with `--yes`; it must report `current`. If it instead reports a conflicting skill already at that destination and was not replaced, re-run the same command with `--force` added — always keep the skill at the current packaged version rather than leaving an older one in place, and this specific replacement needs no separate approval. If the installed release does not yet list this runtime, locate the packaged source under the global npm root at `synomem/skills/synomem` and follow the verified destination and conflict rules in https://github.com/Coaden/synomem/blob/main/docs/skill.md instead. Do not guess a path or create a fake harness home to make an unavailable runtime appear installed.
|
|
128
|
+
5. Inspect the MCP registration command printed by the installer; it launches `synomem mcp --profile <profile>`. Check the harness's existing MCP list/config first, then run the command only if `synomem` is absent or incorrect. Do not create duplicates. Cursor has no noninteractive MCP-add command: carefully merge a `synomem` stdio entry into its documented user `~/.cursor/mcp.json`, using command `synomem` and the arguments `mcp`, `--profile`, `<profile>` (the profile decides the identity, and no secret ever goes in the file); preserve every existing entry.
|
|
148
129
|
6. Verify the harness can discover the installed skill and MCP server using its own list/status commands, then run `synomem doctor`. Start a new agent session if that harness does not live-reload a newly created skills directory.
|
|
149
130
|
7. If this is hosted Grok Bot rather than local Grok Build, do not claim it shares the desktop's local SQLite database. Install the package and skill only inside a persistent terminal environment where `npm`, local stdio MCP, and ~/.grok are actually available. Otherwise provide the skill URL https://github.com/Coaden/synomem/blob/main/skills/synomem/SKILL.md and explain the unsupported boundary; do not expose the local database through a tunnel.
|
|
150
|
-
8. Report the package version,
|
|
131
|
+
8. Report the package version, profile name and its effective context (`synomem whoami`), storage home, installed skill path, MCP registration and verification status, whether a new session is needed, and every file or configuration changed. Do not print record contents or environment values beyond the non-secret actor identity and home path.
|
|
151
132
|
```
|
|
152
133
|
|
|
153
134
|
## TypeScript API
|
|
@@ -212,29 +193,26 @@ watermarks and must not drain historical pages speculatively.
|
|
|
212
193
|
|
|
213
194
|
## MCP
|
|
214
195
|
|
|
215
|
-
|
|
216
|
-
|
|
196
|
+
An MCP server acts as one profile (fixed mode) or a preset of several (explicit mode, where every
|
|
197
|
+
tool call names its `contextId`):
|
|
217
198
|
|
|
218
199
|
```bash
|
|
219
|
-
codex mcp add synomem
|
|
220
|
-
|
|
221
|
-
--env SYNOMEM_ACTOR_KIND=agent \
|
|
222
|
-
--env SYNOMEM_ACTOR_NAME=Codex \
|
|
223
|
-
-- synomem-mcp
|
|
200
|
+
codex mcp add synomem -- synomem mcp --profile gracie-eng
|
|
201
|
+
claude mcp add --scope user synomem -- synomem mcp --preset claude-code --contexts explicit
|
|
224
202
|
```
|
|
225
203
|
|
|
226
|
-
|
|
227
|
-
and lifecycle rules; `synomem_list`, `synomem_get`,
|
|
228
|
-
bounded reads
|
|
204
|
+
Tool arguments can never select an identity; every result reports the `effectiveContext` it ran as.
|
|
205
|
+
Purpose-specific write tools enforce ownership and lifecycle rules; `synomem_list`, `synomem_get`,
|
|
206
|
+
`synomem_changes`, and `synomem_inbox` provide bounded reads, and `synomem_context_list` /
|
|
207
|
+
`synomem_whoami` describe what the connection may act as. See [the MCP guide](docs/mcp.md).
|
|
229
208
|
|
|
230
209
|
## Agent skill
|
|
231
210
|
|
|
232
211
|
The package includes [`skills/synomem`](skills/synomem). Installation is explicit and dry-run first:
|
|
233
212
|
|
|
234
213
|
```bash
|
|
235
|
-
synomem skill install --runtime codex --
|
|
236
|
-
synomem skill install --runtime codex --
|
|
237
|
-
synomem skill install --runtime hermes --agent mycroft --yes
|
|
214
|
+
synomem skill install --runtime codex --profile gracie-eng
|
|
215
|
+
synomem skill install --runtime codex --profile gracie-eng --yes
|
|
238
216
|
synomem skill status
|
|
239
217
|
```
|
|
240
218
|
|
|
@@ -249,10 +227,11 @@ nothing below is written to this machine.
|
|
|
249
227
|
|
|
250
228
|
```text
|
|
251
229
|
~/.synomem/
|
|
252
|
-
├── config.json
|
|
230
|
+
├── config.json # the local store's policy and persistent identity
|
|
231
|
+
├── profiles.json # profiles, connections (references only), presets
|
|
253
232
|
├── synomem.sqlite3
|
|
254
|
-
├── credentials/
|
|
255
|
-
│ └──
|
|
233
|
+
├── credentials/ # only for connections stored with --store file
|
|
234
|
+
│ └── <secret-ref>.json
|
|
256
235
|
└── <handle>/ # one directory per agent, named by handle
|
|
257
236
|
├── profile.json
|
|
258
237
|
├── WINS.md
|
|
@@ -287,8 +266,8 @@ the live database with Git, Dropbox, a network share, or a file-copy tool.
|
|
|
287
266
|
## Trust and privacy
|
|
288
267
|
|
|
289
268
|
Synomem is audit-friendly, not tamper-proof. The local filesystem owner ultimately controls the
|
|
290
|
-
database and configuration.
|
|
291
|
-
prove who launched a process.
|
|
269
|
+
database and configuration. A profile protects ordinary MCP use but does not cryptographically
|
|
270
|
+
prove who launched a process; hosted access is authorized by the API on every request.
|
|
292
271
|
|
|
293
272
|
Do not store credentials, cookies, tokens, authentication headers, environment values, private keys,
|
|
294
273
|
raw sensitive tool output, or unnecessary private content. `public` means eligible for public export;
|
package/dist/backend.d.ts
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { SynomemClient } from './client.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type { ActorIdentity, SynomemConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The person at the keyboard of a local store. The filesystem owner is the
|
|
5
|
+
* ultimate authority over a local store, so administrative commands (creating
|
|
6
|
+
* agents, rebuilding, exporting) run as this actor rather than as an agent.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LOCAL_OPERATOR: ActorIdentity;
|
|
9
|
+
/** A store's config, or undefined when the home has no store yet. */
|
|
5
10
|
export declare function readSynomemConfig(explicitHome?: string, env?: NodeJS.ProcessEnv): SynomemConfig | undefined;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Creates the store's config (with a fresh persistent workspace identity) if it
|
|
13
|
+
* does not exist, and returns it. Idempotent: an existing store keeps its
|
|
14
|
+
* identity, which local context ids are derived from.
|
|
15
|
+
*/
|
|
16
|
+
export declare function ensureLocalStore(explicitHome?: string): SynomemConfig;
|
|
17
|
+
/** The store's persistent workspace identity, creating the store if needed. */
|
|
18
|
+
export declare function localStoreWorkspaceId(explicitHome?: string): string;
|
|
19
|
+
/** Opens (and initializes) a local store as one actor. The caller closes it. */
|
|
20
|
+
export declare function openLocalService(home: string | undefined, actor: ActorIdentity): Promise<SynomemClient>;
|
|
9
21
|
//# sourceMappingURL=backend.d.ts.map
|
package/dist/backend.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAS5C,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,aAI5B,CAAC;AAOF,qEAAqE;AACrE,wBAAgB,iBAAiB,CAC/B,YAAY,CAAC,EAAE,MAAM,EACrB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,aAAa,GAAG,SAAS,CAe3B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,aAAa,CAiBrE;AAED,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,gFAAgF;AAChF,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,EAAE,aAAa,GACnB,OAAO,CAAC,aAAa,CAAC,CAKxB"}
|
package/dist/backend.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local store helpers.
|
|
3
|
+
*
|
|
4
|
+
* A local store is one SQLite home: `<home>/config.json` (its policy and its
|
|
5
|
+
* persistent workspace identity) plus the database beside it. Which store and
|
|
6
|
+
* which actor a command uses is decided by a profile (`profiles.ts`); this
|
|
7
|
+
* module only opens and initializes stores.
|
|
8
|
+
*
|
|
9
|
+
* Remote access no longer passes through here at all. There is no
|
|
10
|
+
* "configured backend" in a store's config any more: a store is always local,
|
|
11
|
+
* and hosted access is a connection plus a context in `profiles.json`.
|
|
12
|
+
*/
|
|
1
13
|
import { chmodSync, existsSync, lstatSync, mkdirSync } from 'node:fs';
|
|
2
14
|
import { join } from 'node:path';
|
|
3
15
|
import { ulid } from 'ulid';
|
|
@@ -5,63 +17,65 @@ import { SynomemClient } from './client.js';
|
|
|
5
17
|
import { defaultConfig, mergeConfig, resolveHome } from './config.js';
|
|
6
18
|
import { SynomemError } from './errors.js';
|
|
7
19
|
import { assertNoSymlinkEscape, atomicWriteFile, ensureDirectory, readJsonFile, } from './fs-utils.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
/**
|
|
21
|
+
* The person at the keyboard of a local store. The filesystem owner is the
|
|
22
|
+
* ultimate authority over a local store, so administrative commands (creating
|
|
23
|
+
* agents, rebuilding, exporting) run as this actor rather than as an agent.
|
|
24
|
+
*/
|
|
25
|
+
export const LOCAL_OPERATOR = {
|
|
26
|
+
kind: 'human',
|
|
27
|
+
id: 'local-cli',
|
|
28
|
+
displayName: 'Local operator',
|
|
29
|
+
};
|
|
30
|
+
function storeLocation(explicitHome) {
|
|
19
31
|
const home = resolveHome(explicitHome);
|
|
20
|
-
return { home,
|
|
32
|
+
return { home, configPath: join(home, 'config.json') };
|
|
21
33
|
}
|
|
34
|
+
/** A store's config, or undefined when the home has no store yet. */
|
|
22
35
|
export function readSynomemConfig(explicitHome, env = process.env) {
|
|
23
|
-
const { home,
|
|
36
|
+
const { home, configPath } = storeLocation(explicitHome);
|
|
24
37
|
if (!existsSync(configPath))
|
|
25
38
|
return undefined;
|
|
26
39
|
if (!existsSync(home) || lstatSync(home).isSymbolicLink()) {
|
|
27
40
|
throw new SynomemError('UNSAFE_PATH', 'The configured Synomem home is unsafe.');
|
|
28
41
|
}
|
|
29
|
-
assertNoSymlinkEscape(home,
|
|
30
|
-
|
|
42
|
+
assertNoSymlinkEscape(home, home);
|
|
43
|
+
const config = mergeConfig(readJsonFile(configPath), undefined, env);
|
|
44
|
+
if (config.backend.kind !== 'local') {
|
|
45
|
+
throw new SynomemError('CONFIG_INVALID', `${configPath} selects a remote backend, which is no longer supported. Hosted access is a connection now: run \`synomem connection login\` and \`synomem profile create\`, or \`synomem setup --backend local\` for a local store.`);
|
|
46
|
+
}
|
|
47
|
+
return config;
|
|
31
48
|
}
|
|
32
|
-
|
|
33
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Creates the store's config (with a fresh persistent workspace identity) if it
|
|
51
|
+
* does not exist, and returns it. Idempotent: an existing store keeps its
|
|
52
|
+
* identity, which local context ids are derived from.
|
|
53
|
+
*/
|
|
54
|
+
export function ensureLocalStore(explicitHome) {
|
|
55
|
+
const { home, configPath } = storeLocation(explicitHome);
|
|
34
56
|
if (!existsSync(home))
|
|
35
57
|
mkdirSync(home, { recursive: true, mode: 0o700 });
|
|
36
58
|
if (lstatSync(home).isSymbolicLink()) {
|
|
37
59
|
throw new SynomemError('UNSAFE_PATH', 'The configured Synomem home cannot be a symbolic link.');
|
|
38
60
|
}
|
|
39
|
-
ensureDirectory(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const config = mergeConfig({ ...existing, backend }, undefined, {});
|
|
61
|
+
ensureDirectory(home);
|
|
62
|
+
chmodSync(home, 0o700);
|
|
63
|
+
const existing = readSynomemConfig(explicitHome, {});
|
|
64
|
+
if (existing)
|
|
65
|
+
return existing;
|
|
66
|
+
const config = mergeConfig({ ...defaultConfig, backend: { kind: 'local' }, workspaceId: ulid() }, undefined, {});
|
|
46
67
|
atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
|
|
47
68
|
return config;
|
|
48
69
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
credentialProvider: env.SYNOMEM_ACCESS_TOKEN
|
|
60
|
-
? environmentCredentialProvider(env)
|
|
61
|
-
: new StoredCredentialProvider(credentialReference(backend.baseUrl, backend.workspaceId, expectedActor), new OsCredentialStore(), env, fetch, resolveHome(options.home)),
|
|
62
|
-
...(options.signal ? { signal: options.signal } : {}),
|
|
63
|
-
...(options.assertActor !== undefined ? { assertActor: options.assertActor } : {}),
|
|
64
|
-
});
|
|
70
|
+
/** The store's persistent workspace identity, creating the store if needed. */
|
|
71
|
+
export function localStoreWorkspaceId(explicitHome) {
|
|
72
|
+
return ensureLocalStore(explicitHome).workspaceId;
|
|
73
|
+
}
|
|
74
|
+
/** Opens (and initializes) a local store as one actor. The caller closes it. */
|
|
75
|
+
export async function openLocalService(home, actor) {
|
|
76
|
+
ensureLocalStore(home);
|
|
77
|
+
const client = new SynomemClient({ ...(home ? { home } : {}), actor });
|
|
78
|
+
await client.init();
|
|
79
|
+
return client;
|
|
65
80
|
}
|
|
66
|
-
export const configuredServiceFactory = (options) => createConfiguredService(options);
|
|
67
81
|
//# sourceMappingURL=backend.js.map
|
package/dist/backend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,YAAY,GACb,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"backend.js","sourceRoot":"","sources":["../src/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,53 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import { type
|
|
4
|
-
import { type PromptIo } from './prompt.js';
|
|
5
|
-
import { type CredentialStore } from './credentials.js';
|
|
6
|
-
import { type OAuthLoginOptions } from './oauth.js';
|
|
3
|
+
import { type CredentialStores, type StoredOAuthCredential } from './credentials.js';
|
|
7
4
|
import { type ImportBundle, type ImportPreview, type ImportResult } from './import.js';
|
|
8
|
-
import type
|
|
9
|
-
import
|
|
5
|
+
import { type OAuthLoginOptions } from './oauth.js';
|
|
6
|
+
import { ProfileStore } from './profiles.js';
|
|
7
|
+
import { type PromptIo } from './prompt.js';
|
|
8
|
+
import { type ContextResolver } from './resolvers.js';
|
|
9
|
+
import type { createLocalResolver, createRemoteResolver } from './resolvers.js';
|
|
10
10
|
export interface CliIo {
|
|
11
11
|
stdout: (text: string) => void;
|
|
12
12
|
stderr: (text: string) => void;
|
|
13
13
|
}
|
|
14
14
|
export interface CliDependencies {
|
|
15
|
-
/** Injected so
|
|
15
|
+
/** Injected so interactive steps can be driven by a test without a terminal. */
|
|
16
16
|
promptIo?: PromptIo;
|
|
17
|
-
credentialStore?: CredentialStore;
|
|
18
|
-
oauthLogin?: (options: OAuthLoginOptions) => Promise<void>;
|
|
19
17
|
env?: NodeJS.ProcessEnv;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
cwd?: string;
|
|
19
|
+
platform?: NodeJS.Platform;
|
|
20
|
+
fetch?: typeof fetch;
|
|
21
|
+
credentialStores?: (home: string) => CredentialStores;
|
|
22
|
+
profileStore?: (home: string) => ProfileStore;
|
|
23
|
+
oauthLogin?: (options: OAuthLoginOptions) => Promise<StoredOAuthCredential>;
|
|
24
|
+
createRemoteResolver?: typeof createRemoteResolver;
|
|
25
|
+
createLocalResolver?: typeof createLocalResolver;
|
|
26
|
+
startMcpServer?: (options: {
|
|
27
|
+
resolver: ContextResolver;
|
|
26
28
|
}) => Promise<void>;
|
|
27
|
-
discoverAccessKeyWorkspaces?: (options: {
|
|
28
|
-
baseUrl: string;
|
|
29
|
-
accessToken: string;
|
|
30
|
-
}) => Promise<{
|
|
31
|
-
organizationId: string;
|
|
32
|
-
workspaces: DiscoveredWorkspace[];
|
|
33
|
-
}>;
|
|
34
|
-
/** Injected so `remote workspace list` can be tested without a network. */
|
|
35
|
-
discoverIdentity?: (options: {
|
|
36
|
-
baseUrl: string;
|
|
37
|
-
accessToken: string;
|
|
38
|
-
}) => Promise<{
|
|
39
|
-
workspaceId: string;
|
|
40
|
-
workspaces: WorkspaceMembership[];
|
|
41
|
-
}>;
|
|
42
29
|
createImportBundle?: (home: string) => Promise<ImportBundle>;
|
|
43
30
|
remoteImport?: (options: {
|
|
44
31
|
baseUrl: string;
|
|
45
32
|
workspaceId: string;
|
|
46
|
-
|
|
33
|
+
contextId: string;
|
|
47
34
|
bundle: ImportBundle;
|
|
48
35
|
planId?: string;
|
|
49
36
|
}) => Promise<ImportPreview | ImportResult>;
|
|
50
37
|
}
|
|
51
|
-
export declare function createCli(io?: CliIo,
|
|
52
|
-
export declare function runCli(argv?: string[], io?: CliIo,
|
|
38
|
+
export declare function createCli(io?: CliIo, dependencies?: CliDependencies): Command;
|
|
39
|
+
export declare function runCli(argv?: string[], io?: CliIo, dependencies?: CliDependencies): Promise<number>;
|
|
53
40
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,OAAO,EAA0B,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAKA,OAAO,EAAE,OAAO,EAA0B,MAAM,WAAW,CAAC;AAU5D,OAAO,EAGL,KAAK,gBAAgB,EAErB,KAAK,qBAAqB,EAC3B,MAAM,kBAAkB,CAAC;AAI1B,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAA6B,KAAK,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EASL,YAAY,EAQb,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAgC,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC1E,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AA0BhF,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,gBAAgB,CAAC;IACtD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC5E,oBAAoB,CAAC,EAAE,OAAO,oBAAoB,CAAC;IACnD,mBAAmB,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACjD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7D,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE;QACvB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,YAAY,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAK,OAAO,CAAC,aAAa,GAAG,YAAY,CAAC,CAAC;CAC7C;AAkOD,wBAAgB,SAAS,CAAC,EAAE,GAAE,KAAiB,EAAE,YAAY,GAAE,eAAoB,GAAG,OAAO,CAyxF5F;AA0BD,wBAAsB,MAAM,CAC1B,IAAI,WAAe,EACnB,EAAE,GAAE,KAAiB,EACrB,YAAY,GAAE,eAAoB,GACjC,OAAO,CAAC,MAAM,CAAC,CAgCjB"}
|