@xmemo/client 0.4.133 → 0.4.135
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/README.md +36 -37
- package/package.json +1 -1
- package/src/cli.js +55 -16
package/README.md
CHANGED
|
@@ -30,10 +30,11 @@ to print the exact command without changing anything.
|
|
|
30
30
|
```bash
|
|
31
31
|
xmemo update
|
|
32
32
|
xmemo setup codex
|
|
33
|
-
xmemo setup codex --
|
|
33
|
+
xmemo setup codex --dry-run
|
|
34
34
|
xmemo setup cursor
|
|
35
|
-
xmemo setup cursor --
|
|
35
|
+
xmemo setup cursor --dry-run
|
|
36
36
|
xmemo setup copilot
|
|
37
|
+
xmemo setup copilot --dry-run
|
|
37
38
|
xmemo doctor
|
|
38
39
|
xmemo discovery show
|
|
39
40
|
xmemo setup
|
|
@@ -142,24 +143,23 @@ accepted as a compatibility alias.
|
|
|
142
143
|
Generate and write a client config from discovery:
|
|
143
144
|
|
|
144
145
|
```bash
|
|
145
|
-
xmemo setup codex
|
|
146
|
-
xmemo setup codex --url "https://your-private-service.example"
|
|
147
|
-
xmemo setup cursor
|
|
146
|
+
xmemo setup codex
|
|
147
|
+
xmemo setup codex --url "https://your-private-service.example"
|
|
148
|
+
xmemo setup cursor
|
|
148
149
|
xmemo setup copilot
|
|
149
150
|
```
|
|
150
151
|
|
|
151
|
-
`xmemo setup <client>` is the unified setup entry point.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
`xmemo setup <client>` is the unified setup entry point. For write-capable
|
|
153
|
+
clients, it applies the user-scoped config directly; use `--dry-run` to preview
|
|
154
|
+
without writing. Generated config references `XMEMO_KEY`; it does not embed the
|
|
155
|
+
token value. Write-capable client configs also include stable non-secret agent
|
|
156
|
+
identity headers where the client format supports them. `--yes` remains accepted
|
|
157
|
+
for Codex and Cursor as a compatibility no-op.
|
|
156
158
|
|
|
157
|
-
`xmemo setup codex` is the recommended Codex path.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
choose a different project instruction file, or `--no-profile` to configure MCP
|
|
162
|
-
only.
|
|
159
|
+
`xmemo setup codex` is the recommended Codex path. It writes the Codex MCP
|
|
160
|
+
config and installs the profile into the current project's `AGENTS.md` marker
|
|
161
|
+
block. Use `--dry-run` to preview, `--profile-target <path>` to choose a
|
|
162
|
+
different project instruction file, or `--no-profile` to configure MCP only.
|
|
163
163
|
|
|
164
164
|
## MCP setup
|
|
165
165
|
|
|
@@ -174,24 +174,25 @@ Current write-capable clients:
|
|
|
174
174
|
```text
|
|
175
175
|
codex ~/.codex/config.toml
|
|
176
176
|
cursor ~/.cursor/mcp.json
|
|
177
|
+
copilot ~/.copilot/mcp-config.json
|
|
177
178
|
```
|
|
178
179
|
|
|
179
180
|
For clients without a verified user-scoped write path, generate a read-only
|
|
180
181
|
template and apply it manually after review:
|
|
181
182
|
|
|
182
183
|
```bash
|
|
183
|
-
xmemo mcp config --client copilot-cli
|
|
184
184
|
xmemo mcp config --client generic --base-url "https://your-private-service.example" --json
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
should only be added after their official user-scoped config format is
|
|
187
|
+
Codex, Cursor, and Copilot CLI have write-capable setup helpers. Other client
|
|
188
|
+
writes should only be added after their official user-scoped config format is
|
|
189
|
+
verified.
|
|
189
190
|
|
|
190
191
|
### Copilot CLI
|
|
191
192
|
|
|
192
|
-
Copilot CLI has `/mcp` management
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
Copilot CLI has `/mcp` management and reads user MCP configuration from
|
|
194
|
+
`~/.copilot/mcp-config.json` (or `$COPILOT_HOME/mcp-config.json`). XMemo writes
|
|
195
|
+
a local proxy server entry there:
|
|
195
196
|
|
|
196
197
|
```bash
|
|
197
198
|
xmemo login
|
|
@@ -199,13 +200,13 @@ xmemo setup copilot
|
|
|
199
200
|
xmemo mcp proxy
|
|
200
201
|
```
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
does not include token or identity headers. `xmemo
|
|
204
|
-
|
|
205
|
-
token
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
203
|
+
`xmemo setup copilot` writes `memory-os` to Copilot CLI's user MCP config and
|
|
204
|
+
does not include token or identity headers. Use `xmemo setup copilot --dry-run`
|
|
205
|
+
to preview without writing. `xmemo mcp proxy` reads the token saved by
|
|
206
|
+
`xmemo login` or `xmemo token add --from-stdin`, adds the XMemo bearer token and
|
|
207
|
+
local agent identity, then forwards requests to `https://xmemo.dev/mcp`. If
|
|
208
|
+
Copilot CLI is already open, reload MCP config or restart Copilot CLI after
|
|
209
|
+
setup.
|
|
209
210
|
If you specifically want the older environment-variable template, run:
|
|
210
211
|
|
|
211
212
|
```bash
|
|
@@ -218,14 +219,12 @@ Recommended Codex setup:
|
|
|
218
219
|
|
|
219
220
|
```bash
|
|
220
221
|
xmemo setup codex
|
|
221
|
-
xmemo setup codex --yes
|
|
222
222
|
xmemo smoke --client codex
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
`setup codex`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
`AGENTS.md` between these markers:
|
|
225
|
+
`setup codex` writes the MCP config to user-scoped Codex config and installs the
|
|
226
|
+
XMemo Codex behavior profile into the current project's `AGENTS.md` between
|
|
227
|
+
these markers. Use `xmemo setup codex --dry-run` to preview without writing.
|
|
229
228
|
|
|
230
229
|
```html
|
|
231
230
|
<!-- memory-os:codex-profile:start -->
|
|
@@ -277,11 +276,11 @@ Recommended Cursor setup:
|
|
|
277
276
|
|
|
278
277
|
```bash
|
|
279
278
|
xmemo setup cursor
|
|
280
|
-
xmemo setup cursor --yes
|
|
281
279
|
```
|
|
282
280
|
|
|
283
|
-
`setup cursor`
|
|
284
|
-
|
|
281
|
+
`setup cursor` merges the Cursor MCP config into the default Cursor user config
|
|
282
|
+
path. Use `xmemo setup cursor --dry-run` to preview without writing. The
|
|
283
|
+
lower-level equivalent remains:
|
|
285
284
|
|
|
286
285
|
```bash
|
|
287
286
|
xmemo mcp add cursor --url "$XMEMO_URL" --write
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ const PACKAGE_NAME = '@xmemo/client';
|
|
|
10
10
|
const FALLBACK_PACKAGE_NAME = '@yonro/xmemo-client';
|
|
11
11
|
const COMMAND_NAME = 'xmemo';
|
|
12
12
|
const LEGACY_COMMAND_NAME = 'memory-os';
|
|
13
|
-
const CLI_VERSION = '0.4.
|
|
13
|
+
const CLI_VERSION = '0.4.135';
|
|
14
14
|
const DEFAULT_SERVICE_URL = 'https://xmemo.dev';
|
|
15
15
|
const TOKEN_ENV_VAR = 'XMEMO_KEY';
|
|
16
16
|
const LEGACY_TOKEN_ENV_VAR = 'MEMORY_OS_MCP_TOKEN';
|
|
@@ -157,7 +157,7 @@ function writeHelp(io) {
|
|
|
157
157
|
writeLine(io.stdout, ` ${COMMAND_NAME} update [--dry-run] [--json]`);
|
|
158
158
|
writeLine(io.stdout, ` ${COMMAND_NAME} doctor [--base-url <https://api.example.com>] [--json]`);
|
|
159
159
|
writeLine(io.stdout, ` ${COMMAND_NAME} discovery show [--base-url <https://api.example.com>] [--json]`);
|
|
160
|
-
writeLine(io.stdout, ` ${COMMAND_NAME} setup <codex|cursor|copilot> [--url <https://api.example.com>] [--
|
|
160
|
+
writeLine(io.stdout, ` ${COMMAND_NAME} setup <codex|cursor|copilot> [--url <https://api.example.com>] [--dry-run] [--json]`);
|
|
161
161
|
writeLine(io.stdout, ` ${COMMAND_NAME} login [--from-stdin] [--base-url <url>] [--timeout-ms <ms>] [--http-timeout-ms <ms>] [--json]`);
|
|
162
162
|
writeLine(io.stdout, ` ${COMMAND_NAME} auth status [--verify] [--base-url <url>] [--json]`);
|
|
163
163
|
writeLine(io.stdout, ` ${COMMAND_NAME} status [--url <https://api.example.com>] [--json]`);
|
|
@@ -367,8 +367,9 @@ async function setupCommand(args, io) {
|
|
|
367
367
|
const baseUrl = normalizeBaseUrl(baseUrlOption(optionArgs, io.env));
|
|
368
368
|
const outputJson = hasFlag(optionArgs, '--json');
|
|
369
369
|
const shortClientSetup = Boolean(positionalClientId);
|
|
370
|
-
const writeConfig = hasFlag(optionArgs, '--write') || (shortClientSetup && hasFlag(optionArgs, '--yes'));
|
|
371
370
|
const clientId = normalizeSetupClientId(positionalClientId ?? optionValue(optionArgs, '--client'));
|
|
371
|
+
const dryRun = hasFlag(optionArgs, '--dry-run') || hasFlag(optionArgs, '--preview');
|
|
372
|
+
const writeConfig = !dryRun && (hasFlag(optionArgs, '--write') || hasFlag(optionArgs, '--yes') || shortClientSetup);
|
|
372
373
|
const timeoutMs = parsePositiveInteger(optionValue(optionArgs, '--timeout-ms') ?? '5000', '--timeout-ms');
|
|
373
374
|
const installProfile = shortClientSetup
|
|
374
375
|
&& clientId === 'codex'
|
|
@@ -391,11 +392,12 @@ async function setupCommand(args, io) {
|
|
|
391
392
|
|
|
392
393
|
if (clientId) {
|
|
393
394
|
if (clientId === 'copilot-cli') {
|
|
395
|
+
const proxyPort = parsePositiveInteger(optionValue(optionArgs, '--port') ?? String(DEFAULT_PROXY_PORT), '--port');
|
|
396
|
+
setupPlan.selectedClient = copilotSetupPlan(setupPlan.mcpUrl, proxyPort, io.env);
|
|
394
397
|
if (writeConfig) {
|
|
395
|
-
|
|
398
|
+
await mergeCopilotMcpConfig(setupPlan.selectedClient.configPath, setupPlan.selectedClient.proxyUrl);
|
|
399
|
+
setupPlan.selectedClient.written = true;
|
|
396
400
|
}
|
|
397
|
-
const proxyPort = parsePositiveInteger(optionValue(optionArgs, '--port') ?? String(DEFAULT_PROXY_PORT), '--port');
|
|
398
|
-
setupPlan.selectedClient = copilotSetupPlan(setupPlan.mcpUrl, proxyPort);
|
|
399
401
|
} else {
|
|
400
402
|
const client = MCP_CLIENTS.get(clientId);
|
|
401
403
|
if (!client) {
|
|
@@ -1425,14 +1427,14 @@ function clientSetupPlan(clientId, client, mcpUrl, env, identity) {
|
|
|
1425
1427
|
};
|
|
1426
1428
|
}
|
|
1427
1429
|
|
|
1428
|
-
function copilotSetupPlan(mcpUrl, proxyPort) {
|
|
1430
|
+
function copilotSetupPlan(mcpUrl, proxyPort, env) {
|
|
1429
1431
|
const proxyUrl = `http://${DEFAULT_PROXY_HOST}:${proxyPort}/mcp`;
|
|
1430
1432
|
const template = mcpLocalProxyTemplate('copilot-cli', proxyUrl);
|
|
1431
1433
|
return {
|
|
1432
1434
|
id: 'copilot-cli',
|
|
1433
1435
|
label: 'Copilot CLI',
|
|
1434
1436
|
configKind: 'local-proxy',
|
|
1435
|
-
configPath:
|
|
1437
|
+
configPath: defaultCopilotConfigPath(env),
|
|
1436
1438
|
serverName: template.serverName,
|
|
1437
1439
|
mcpUrl,
|
|
1438
1440
|
proxyUrl,
|
|
@@ -1442,7 +1444,7 @@ function copilotSetupPlan(mcpUrl, proxyPort) {
|
|
|
1442
1444
|
template: template.snippet,
|
|
1443
1445
|
agentId: template.agentIdentity.agentId,
|
|
1444
1446
|
writesTokenValue: false,
|
|
1445
|
-
writeSupported:
|
|
1447
|
+
writeSupported: true,
|
|
1446
1448
|
written: false
|
|
1447
1449
|
};
|
|
1448
1450
|
}
|
|
@@ -1478,9 +1480,14 @@ function writeSetupSummary(plan, io) {
|
|
|
1478
1480
|
}
|
|
1479
1481
|
if (plan.selectedClient.configKind === 'local-proxy') {
|
|
1480
1482
|
writeLine(io.stdout, ` Local proxy: ${plan.selectedClient.requiresLocalCommand}`);
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1483
|
+
if (plan.selectedClient.written) {
|
|
1484
|
+
writeLine(io.stdout, ` Next: keep \`${plan.selectedClient.requiresLocalCommand}\` running while you use Copilot CLI.`);
|
|
1485
|
+
writeLine(io.stdout, ' If Copilot CLI is already open, reload MCP config or restart Copilot CLI.');
|
|
1486
|
+
} else {
|
|
1487
|
+
writeLine(io.stdout, ' MCP template:');
|
|
1488
|
+
writeLine(io.stdout, JSON.stringify(plan.selectedClient.template, null, 2));
|
|
1489
|
+
writeLine(io.stdout, ` Next: ${COMMAND_NAME} setup copilot --url ${plan.baseUrl}`);
|
|
1490
|
+
}
|
|
1484
1491
|
return;
|
|
1485
1492
|
}
|
|
1486
1493
|
if (plan.selectedClient.codexProfile) {
|
|
@@ -1493,7 +1500,7 @@ function writeSetupSummary(plan, io) {
|
|
|
1493
1500
|
}
|
|
1494
1501
|
}
|
|
1495
1502
|
if (!plan.selectedClient.written) {
|
|
1496
|
-
writeLine(io.stdout, ` Next: ${COMMAND_NAME} setup ${plan.selectedClient.id} --url ${plan.baseUrl}
|
|
1503
|
+
writeLine(io.stdout, ` Next: ${COMMAND_NAME} setup ${plan.selectedClient.id} --url ${plan.baseUrl}`);
|
|
1497
1504
|
}
|
|
1498
1505
|
return;
|
|
1499
1506
|
}
|
|
@@ -1501,7 +1508,7 @@ function writeSetupSummary(plan, io) {
|
|
|
1501
1508
|
writeLine(io.stdout, '');
|
|
1502
1509
|
writeLine(io.stdout, 'Next steps:');
|
|
1503
1510
|
writeLine(io.stdout, ` 1. Create a scoped token in the token portal and store it in ${plan.tokenEnvVar}.`);
|
|
1504
|
-
writeLine(io.stdout, ` 2. Configure a client, for example: ${COMMAND_NAME} setup codex --url ${plan.baseUrl}
|
|
1511
|
+
writeLine(io.stdout, ` 2. Configure a client, for example: ${COMMAND_NAME} setup codex --url ${plan.baseUrl}`);
|
|
1505
1512
|
writeLine(io.stdout, ` 3. Run ${COMMAND_NAME} status to smoke-test the service without sending the token.`);
|
|
1506
1513
|
}
|
|
1507
1514
|
|
|
@@ -1566,7 +1573,7 @@ function codexMemoryProfile() {
|
|
|
1566
1573
|
'For routine or low-signal output, skip durable writes. Prefer summarized procedural or semantic memories over verbose logs.',
|
|
1567
1574
|
'Keep XMemo authentication through the XMEMO_KEY environment variable; do not paste token values into prompts, config files, or logs.'
|
|
1568
1575
|
],
|
|
1569
|
-
setupCommand: `${COMMAND_NAME} setup codex --url "$XMEMO_URL"
|
|
1576
|
+
setupCommand: `${COMMAND_NAME} setup codex --url "$XMEMO_URL"`,
|
|
1570
1577
|
smokeCommand: `${COMMAND_NAME} smoke --client codex`
|
|
1571
1578
|
};
|
|
1572
1579
|
}
|
|
@@ -1873,6 +1880,31 @@ async function mergeJsonMcpConfig(configPath, mcpUrl, identity) {
|
|
|
1873
1880
|
await bestEffortChmod(configPath, 0o600);
|
|
1874
1881
|
}
|
|
1875
1882
|
|
|
1883
|
+
async function mergeCopilotMcpConfig(configPath, proxyUrl) {
|
|
1884
|
+
const existing = await readTextIfExists(configPath);
|
|
1885
|
+
const parsed = existing.trim().length === 0 ? {} : parseJsonConfig(existing, configPath);
|
|
1886
|
+
|
|
1887
|
+
if (!isPlainObject(parsed)) {
|
|
1888
|
+
throw new UsageError(`Copilot MCP JSON config must be an object: ${configPath}`);
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if (!isPlainObject(parsed.mcpServers)) {
|
|
1892
|
+
parsed.mcpServers = {};
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
parsed.mcpServers['memory-os'] = copilotLocalProxyServerConfig(proxyUrl);
|
|
1896
|
+
await fs.mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });
|
|
1897
|
+
await fs.writeFile(configPath, `${JSON.stringify(parsed, null, 2)}\n`, { mode: 0o600 });
|
|
1898
|
+
await bestEffortChmod(configPath, 0o600);
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
function copilotLocalProxyServerConfig(proxyUrl) {
|
|
1902
|
+
return {
|
|
1903
|
+
type: 'http',
|
|
1904
|
+
url: proxyUrl
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1876
1908
|
function cursorJsonConfig(mcpUrl, identity = envReferenceIdentity('cursor')) {
|
|
1877
1909
|
return {
|
|
1878
1910
|
mcpServers: {
|
|
@@ -1939,11 +1971,13 @@ function envReferenceIdentity(clientId) {
|
|
|
1939
1971
|
}
|
|
1940
1972
|
|
|
1941
1973
|
function supportedMcpClients() {
|
|
1942
|
-
|
|
1974
|
+
const clients = Array.from(MCP_CLIENTS.entries()).map(([id, client]) => ({
|
|
1943
1975
|
id,
|
|
1944
1976
|
label: client.label,
|
|
1945
1977
|
configKind: client.configKind
|
|
1946
1978
|
}));
|
|
1979
|
+
clients.push({ id: 'copilot-cli', label: 'Copilot CLI', configKind: 'local-proxy' });
|
|
1980
|
+
return clients;
|
|
1947
1981
|
}
|
|
1948
1982
|
|
|
1949
1983
|
function supportedMcpClientIds() {
|
|
@@ -1989,6 +2023,11 @@ function defaultCursorConfigPath(env) {
|
|
|
1989
2023
|
return path.join(home, '.cursor', 'mcp.json');
|
|
1990
2024
|
}
|
|
1991
2025
|
|
|
2026
|
+
function defaultCopilotConfigPath(env) {
|
|
2027
|
+
const home = env.USERPROFILE || env.HOME || os.homedir();
|
|
2028
|
+
return path.join(env.COPILOT_HOME ?? path.join(home, '.copilot'), 'mcp-config.json');
|
|
2029
|
+
}
|
|
2030
|
+
|
|
1992
2031
|
async function writePlaintextCredential(credentialPath, token, metadata = {}) {
|
|
1993
2032
|
await fs.mkdir(path.dirname(credentialPath), { recursive: true, mode: 0o700 });
|
|
1994
2033
|
await bestEffortChmod(path.dirname(credentialPath), 0o700);
|