@xmemo/client 0.4.132 → 0.4.134
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 +31 -26
- package/package.json +1 -1
- package/src/cli.js +87 -27
package/README.md
CHANGED
|
@@ -30,8 +30,10 @@ to print the exact command without changing anything.
|
|
|
30
30
|
```bash
|
|
31
31
|
xmemo update
|
|
32
32
|
xmemo setup codex
|
|
33
|
-
xmemo setup codex --
|
|
34
|
-
xmemo
|
|
33
|
+
xmemo setup codex --dry-run
|
|
34
|
+
xmemo setup cursor
|
|
35
|
+
xmemo setup cursor --dry-run
|
|
36
|
+
xmemo setup copilot
|
|
35
37
|
xmemo doctor
|
|
36
38
|
xmemo discovery show
|
|
37
39
|
xmemo setup
|
|
@@ -44,7 +46,7 @@ xmemo env example --shell bash
|
|
|
44
46
|
xmemo mcp list
|
|
45
47
|
xmemo mcp config --client generic
|
|
46
48
|
xmemo profile status codex
|
|
47
|
-
xmemo
|
|
49
|
+
xmemo smoke --client codex
|
|
48
50
|
xmemo privacy
|
|
49
51
|
```
|
|
50
52
|
|
|
@@ -140,23 +142,23 @@ accepted as a compatibility alias.
|
|
|
140
142
|
Generate and write a client config from discovery:
|
|
141
143
|
|
|
142
144
|
```bash
|
|
143
|
-
xmemo setup codex
|
|
144
|
-
xmemo setup codex --url "https://your-private-service.example"
|
|
145
|
-
xmemo setup
|
|
146
|
-
xmemo setup
|
|
145
|
+
xmemo setup codex
|
|
146
|
+
xmemo setup codex --url "https://your-private-service.example"
|
|
147
|
+
xmemo setup cursor
|
|
148
|
+
xmemo setup copilot
|
|
147
149
|
```
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
`xmemo setup <client>` is the unified setup entry point. For write-capable
|
|
152
|
+
clients, it applies the user-scoped config directly; use `--dry-run` to preview
|
|
153
|
+
without writing. Generated config references `XMEMO_KEY`; it does not embed the
|
|
154
|
+
token value. Write-capable client configs also include stable non-secret agent
|
|
155
|
+
identity headers where the client format supports them. `--yes` remains accepted
|
|
156
|
+
for Codex and Cursor as a compatibility no-op.
|
|
153
157
|
|
|
154
|
-
`xmemo setup codex` is the recommended Codex path.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
choose a different project instruction file, or `--no-profile` to configure MCP
|
|
159
|
-
only.
|
|
158
|
+
`xmemo setup codex` is the recommended Codex path. It writes the Codex MCP
|
|
159
|
+
config and installs the profile into the current project's `AGENTS.md` marker
|
|
160
|
+
block. Use `--dry-run` to preview, `--profile-target <path>` to choose a
|
|
161
|
+
different project instruction file, or `--no-profile` to configure MCP only.
|
|
160
162
|
|
|
161
163
|
## MCP setup
|
|
162
164
|
|
|
@@ -192,7 +194,7 @@ directly. The recommended personal-user path is therefore local proxy mode:
|
|
|
192
194
|
|
|
193
195
|
```bash
|
|
194
196
|
xmemo login
|
|
195
|
-
xmemo
|
|
197
|
+
xmemo setup copilot
|
|
196
198
|
xmemo mcp proxy
|
|
197
199
|
```
|
|
198
200
|
|
|
@@ -200,6 +202,9 @@ The generated Copilot CLI template points at `http://127.0.0.1:8765/mcp` and
|
|
|
200
202
|
does not include token or identity headers. `xmemo mcp proxy` reads the token
|
|
201
203
|
saved by `xmemo login` or `xmemo token add --from-stdin`, adds the XMemo bearer
|
|
202
204
|
token and local agent identity, then forwards requests to `https://xmemo.dev/mcp`.
|
|
205
|
+
Copilot CLI does not currently document a stable cross-platform config file for
|
|
206
|
+
third-party tools to edit, so `xmemo setup copilot` prints the template and next
|
|
207
|
+
command instead of writing directly.
|
|
203
208
|
If you specifically want the older environment-variable template, run:
|
|
204
209
|
|
|
205
210
|
```bash
|
|
@@ -212,14 +217,12 @@ Recommended Codex setup:
|
|
|
212
217
|
|
|
213
218
|
```bash
|
|
214
219
|
xmemo setup codex
|
|
215
|
-
xmemo setup codex --yes
|
|
216
220
|
xmemo smoke --client codex
|
|
217
221
|
```
|
|
218
222
|
|
|
219
|
-
`setup codex`
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
`AGENTS.md` between these markers:
|
|
223
|
+
`setup codex` writes the MCP config to user-scoped Codex config and installs the
|
|
224
|
+
XMemo Codex behavior profile into the current project's `AGENTS.md` between
|
|
225
|
+
these markers. Use `xmemo setup codex --dry-run` to preview without writing.
|
|
223
226
|
|
|
224
227
|
```html
|
|
225
228
|
<!-- memory-os:codex-profile:start -->
|
|
@@ -267,13 +270,15 @@ absence of embedded token values.
|
|
|
267
270
|
|
|
268
271
|
### Cursor
|
|
269
272
|
|
|
270
|
-
|
|
273
|
+
Recommended Cursor setup:
|
|
271
274
|
|
|
272
275
|
```bash
|
|
273
|
-
xmemo
|
|
276
|
+
xmemo setup cursor
|
|
274
277
|
```
|
|
275
278
|
|
|
276
|
-
|
|
279
|
+
`setup cursor` merges the Cursor MCP config into the default Cursor user config
|
|
280
|
+
path. Use `xmemo setup cursor --dry-run` to preview without writing. The
|
|
281
|
+
lower-level equivalent remains:
|
|
277
282
|
|
|
278
283
|
```bash
|
|
279
284
|
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.134';
|
|
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';
|
|
@@ -44,6 +44,13 @@ const MCP_CLIENTS = new Map([
|
|
|
44
44
|
}]
|
|
45
45
|
]);
|
|
46
46
|
|
|
47
|
+
const SETUP_CLIENT_ALIASES = new Map([
|
|
48
|
+
['codex', 'codex'],
|
|
49
|
+
['cursor', 'cursor'],
|
|
50
|
+
['copilot', 'copilot-cli'],
|
|
51
|
+
['copilot-cli', 'copilot-cli']
|
|
52
|
+
]);
|
|
53
|
+
|
|
47
54
|
class UsageError extends Error {
|
|
48
55
|
constructor(message) {
|
|
49
56
|
super(message);
|
|
@@ -150,7 +157,7 @@ function writeHelp(io) {
|
|
|
150
157
|
writeLine(io.stdout, ` ${COMMAND_NAME} update [--dry-run] [--json]`);
|
|
151
158
|
writeLine(io.stdout, ` ${COMMAND_NAME} doctor [--base-url <https://api.example.com>] [--json]`);
|
|
152
159
|
writeLine(io.stdout, ` ${COMMAND_NAME} discovery show [--base-url <https://api.example.com>] [--json]`);
|
|
153
|
-
writeLine(io.stdout, ` ${COMMAND_NAME} setup
|
|
160
|
+
writeLine(io.stdout, ` ${COMMAND_NAME} setup <codex|cursor|copilot> [--url <https://api.example.com>] [--dry-run] [--json]`);
|
|
154
161
|
writeLine(io.stdout, ` ${COMMAND_NAME} login [--from-stdin] [--base-url <url>] [--timeout-ms <ms>] [--http-timeout-ms <ms>] [--json]`);
|
|
155
162
|
writeLine(io.stdout, ` ${COMMAND_NAME} auth status [--verify] [--base-url <url>] [--json]`);
|
|
156
163
|
writeLine(io.stdout, ` ${COMMAND_NAME} status [--url <https://api.example.com>] [--json]`);
|
|
@@ -360,8 +367,9 @@ async function setupCommand(args, io) {
|
|
|
360
367
|
const baseUrl = normalizeBaseUrl(baseUrlOption(optionArgs, io.env));
|
|
361
368
|
const outputJson = hasFlag(optionArgs, '--json');
|
|
362
369
|
const shortClientSetup = Boolean(positionalClientId);
|
|
363
|
-
const
|
|
364
|
-
const
|
|
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') || (shortClientSetup && clientId !== 'copilot-cli'));
|
|
365
373
|
const timeoutMs = parsePositiveInteger(optionValue(optionArgs, '--timeout-ms') ?? '5000', '--timeout-ms');
|
|
366
374
|
const installProfile = shortClientSetup
|
|
367
375
|
&& clientId === 'codex'
|
|
@@ -383,24 +391,32 @@ async function setupCommand(args, io) {
|
|
|
383
391
|
const setupPlan = buildSetupPlan({ baseUrl, discoveryUrl, statusUrl, discovery, status });
|
|
384
392
|
|
|
385
393
|
if (clientId) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
394
|
+
if (clientId === 'copilot-cli') {
|
|
395
|
+
if (hasFlag(optionArgs, '--write') || hasFlag(optionArgs, '--yes')) {
|
|
396
|
+
throw new UsageError(`Copilot CLI setup cannot be written automatically yet. Run \`${COMMAND_NAME} setup copilot\` to print the local proxy template, then add it with Copilot CLI MCP management.`);
|
|
397
|
+
}
|
|
398
|
+
const proxyPort = parsePositiveInteger(optionValue(optionArgs, '--port') ?? String(DEFAULT_PROXY_PORT), '--port');
|
|
399
|
+
setupPlan.selectedClient = copilotSetupPlan(setupPlan.mcpUrl, proxyPort);
|
|
400
|
+
} else {
|
|
401
|
+
const client = MCP_CLIENTS.get(clientId);
|
|
402
|
+
if (!client) {
|
|
403
|
+
throw new UsageError(`Unsupported MCP client: ${clientId}. Supported clients: ${supportedSetupClientIds().join(', ')}.`);
|
|
404
|
+
}
|
|
390
405
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
406
|
+
const identity = writeConfig ? await agentIdentity(clientId, io.env) : envReferenceIdentity(clientId);
|
|
407
|
+
setupPlan.selectedClient = clientSetupPlan(clientId, client, setupPlan.mcpUrl, io.env, identity);
|
|
408
|
+
if (writeConfig) {
|
|
409
|
+
await client.writeConfig(setupPlan.selectedClient.configPath, setupPlan.mcpUrl, identity);
|
|
410
|
+
setupPlan.selectedClient.written = true;
|
|
411
|
+
}
|
|
397
412
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
413
|
+
if (clientId === 'codex' && shortClientSetup) {
|
|
414
|
+
const profileTarget = optionValue(optionArgs, '--profile-target')
|
|
415
|
+
?? optionValue(optionArgs, '--target')
|
|
416
|
+
?? defaultCodexProfileTarget();
|
|
417
|
+
const profileResult = await codexProfileInstallResult(profileTarget, { write: installProfile });
|
|
418
|
+
setupPlan.selectedClient.codexProfile = profileResult;
|
|
419
|
+
}
|
|
404
420
|
}
|
|
405
421
|
}
|
|
406
422
|
|
|
@@ -1410,6 +1426,28 @@ function clientSetupPlan(clientId, client, mcpUrl, env, identity) {
|
|
|
1410
1426
|
};
|
|
1411
1427
|
}
|
|
1412
1428
|
|
|
1429
|
+
function copilotSetupPlan(mcpUrl, proxyPort) {
|
|
1430
|
+
const proxyUrl = `http://${DEFAULT_PROXY_HOST}:${proxyPort}/mcp`;
|
|
1431
|
+
const template = mcpLocalProxyTemplate('copilot-cli', proxyUrl);
|
|
1432
|
+
return {
|
|
1433
|
+
id: 'copilot-cli',
|
|
1434
|
+
label: 'Copilot CLI',
|
|
1435
|
+
configKind: 'local-proxy',
|
|
1436
|
+
configPath: 'Copilot CLI MCP config',
|
|
1437
|
+
serverName: template.serverName,
|
|
1438
|
+
mcpUrl,
|
|
1439
|
+
proxyUrl,
|
|
1440
|
+
tokenEnvVar: TOKEN_ENV_VAR,
|
|
1441
|
+
requiresCredential: template.requiresCredential,
|
|
1442
|
+
requiresLocalCommand: template.requiresLocalCommand,
|
|
1443
|
+
template: template.snippet,
|
|
1444
|
+
agentId: template.agentIdentity.agentId,
|
|
1445
|
+
writesTokenValue: false,
|
|
1446
|
+
writeSupported: false,
|
|
1447
|
+
written: false
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1413
1451
|
function writeSetupSummary(plan, io) {
|
|
1414
1452
|
writeLine(io.stdout, `${PRODUCT_NAME} setup discovery: ${plan.baseUrl}`);
|
|
1415
1453
|
writeLine(io.stdout, ` API: ${plan.apiBase}`);
|
|
@@ -1436,7 +1474,16 @@ function writeSetupSummary(plan, io) {
|
|
|
1436
1474
|
writeLine(io.stdout, ` Written: ${plan.selectedClient.written}`);
|
|
1437
1475
|
writeLine(io.stdout, ` Token value embedded: ${plan.selectedClient.writesTokenValue}`);
|
|
1438
1476
|
writeLine(io.stdout, ` Agent ID: ${plan.selectedClient.agentId}`);
|
|
1439
|
-
|
|
1477
|
+
if (plan.selectedClient.agentInstanceIdPath) {
|
|
1478
|
+
writeLine(io.stdout, ` Agent instance ID stored: ${plan.selectedClient.agentInstanceIdPath}`);
|
|
1479
|
+
}
|
|
1480
|
+
if (plan.selectedClient.configKind === 'local-proxy') {
|
|
1481
|
+
writeLine(io.stdout, ` Local proxy: ${plan.selectedClient.requiresLocalCommand}`);
|
|
1482
|
+
writeLine(io.stdout, ' MCP template:');
|
|
1483
|
+
writeLine(io.stdout, JSON.stringify(plan.selectedClient.template, null, 2));
|
|
1484
|
+
writeLine(io.stdout, ` Next: add the template with Copilot CLI MCP management, then keep \`${plan.selectedClient.requiresLocalCommand}\` running while you use Copilot CLI.`);
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1440
1487
|
if (plan.selectedClient.codexProfile) {
|
|
1441
1488
|
const profile = plan.selectedClient.codexProfile;
|
|
1442
1489
|
writeLine(io.stdout, ` Codex profile target: ${profile.targetPath}`);
|
|
@@ -1447,7 +1494,7 @@ function writeSetupSummary(plan, io) {
|
|
|
1447
1494
|
}
|
|
1448
1495
|
}
|
|
1449
1496
|
if (!plan.selectedClient.written) {
|
|
1450
|
-
writeLine(io.stdout, ` Next: ${COMMAND_NAME}
|
|
1497
|
+
writeLine(io.stdout, ` Next: ${COMMAND_NAME} setup ${plan.selectedClient.id} --url ${plan.baseUrl}`);
|
|
1451
1498
|
}
|
|
1452
1499
|
return;
|
|
1453
1500
|
}
|
|
@@ -1455,7 +1502,7 @@ function writeSetupSummary(plan, io) {
|
|
|
1455
1502
|
writeLine(io.stdout, '');
|
|
1456
1503
|
writeLine(io.stdout, 'Next steps:');
|
|
1457
1504
|
writeLine(io.stdout, ` 1. Create a scoped token in the token portal and store it in ${plan.tokenEnvVar}.`);
|
|
1458
|
-
writeLine(io.stdout, ` 2. Configure a client, for example: ${COMMAND_NAME} setup --url ${plan.baseUrl}
|
|
1505
|
+
writeLine(io.stdout, ` 2. Configure a client, for example: ${COMMAND_NAME} setup codex --url ${plan.baseUrl}`);
|
|
1459
1506
|
writeLine(io.stdout, ` 3. Run ${COMMAND_NAME} status to smoke-test the service without sending the token.`);
|
|
1460
1507
|
}
|
|
1461
1508
|
|
|
@@ -1520,7 +1567,7 @@ function codexMemoryProfile() {
|
|
|
1520
1567
|
'For routine or low-signal output, skip durable writes. Prefer summarized procedural or semantic memories over verbose logs.',
|
|
1521
1568
|
'Keep XMemo authentication through the XMEMO_KEY environment variable; do not paste token values into prompts, config files, or logs.'
|
|
1522
1569
|
],
|
|
1523
|
-
setupCommand: `${COMMAND_NAME} setup codex --url "$XMEMO_URL"
|
|
1570
|
+
setupCommand: `${COMMAND_NAME} setup codex --url "$XMEMO_URL"`,
|
|
1524
1571
|
smokeCommand: `${COMMAND_NAME} smoke --client codex`
|
|
1525
1572
|
};
|
|
1526
1573
|
}
|
|
@@ -1904,6 +1951,10 @@ function supportedMcpClientIds() {
|
|
|
1904
1951
|
return Array.from(MCP_CLIENTS.keys());
|
|
1905
1952
|
}
|
|
1906
1953
|
|
|
1954
|
+
function supportedSetupClientIds() {
|
|
1955
|
+
return ['codex', 'cursor', 'copilot'];
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1907
1958
|
function credentialsPath(env) {
|
|
1908
1959
|
return path.join(configRoot(env), 'credentials.json');
|
|
1909
1960
|
}
|
|
@@ -1990,11 +2041,20 @@ function positionalClientArg(args) {
|
|
|
1990
2041
|
return null;
|
|
1991
2042
|
}
|
|
1992
2043
|
|
|
1993
|
-
|
|
1994
|
-
|
|
2044
|
+
return normalizeSetupClientId(candidate);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
function normalizeSetupClientId(candidate) {
|
|
2048
|
+
if (!candidate) {
|
|
2049
|
+
return null;
|
|
2050
|
+
}
|
|
2051
|
+
|
|
2052
|
+
const normalized = SETUP_CLIENT_ALIASES.get(candidate);
|
|
2053
|
+
if (!normalized) {
|
|
2054
|
+
throw new UsageError(`Unsupported setup client: ${candidate}. Supported clients: ${supportedSetupClientIds().join(', ')}.`);
|
|
1995
2055
|
}
|
|
1996
2056
|
|
|
1997
|
-
return
|
|
2057
|
+
return normalized;
|
|
1998
2058
|
}
|
|
1999
2059
|
|
|
2000
2060
|
function optionValue(args, name) {
|