@xmemo/client 0.4.127 → 0.4.128
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 +12 -0
- package/package.json +1 -1
- package/src/cli.js +89 -2
package/README.md
CHANGED
|
@@ -16,9 +16,21 @@ internal scripts are not part of this npm package.
|
|
|
16
16
|
npm install -g @xmemo/client
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
Upgrade an existing global install:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
xmemo update
|
|
23
|
+
# or
|
|
24
|
+
xmemo --update
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Both commands run `npm install -g @xmemo/client@latest`. Use
|
|
28
|
+
`xmemo update --dry-run` to print the exact command without changing anything.
|
|
29
|
+
|
|
19
30
|
## Commands
|
|
20
31
|
|
|
21
32
|
```bash
|
|
33
|
+
xmemo update
|
|
22
34
|
xmemo setup codex
|
|
23
35
|
xmemo setup codex --yes
|
|
24
36
|
xmemo smoke --client codex
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import http from 'node:http';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
5
6
|
import { randomUUID } from 'node:crypto';
|
|
6
7
|
|
|
7
8
|
const PRODUCT_NAME = 'XMemo';
|
|
@@ -9,7 +10,7 @@ const PACKAGE_NAME = '@xmemo/client';
|
|
|
9
10
|
const FALLBACK_PACKAGE_NAME = '@yonro/xmemo-client';
|
|
10
11
|
const COMMAND_NAME = 'xmemo';
|
|
11
12
|
const LEGACY_COMMAND_NAME = 'memory-os';
|
|
12
|
-
const CLI_VERSION = '0.4.
|
|
13
|
+
const CLI_VERSION = '0.4.128';
|
|
13
14
|
const DEFAULT_SERVICE_URL = 'https://xmemo.dev';
|
|
14
15
|
const TOKEN_ENV_VAR = 'XMEMO_KEY';
|
|
15
16
|
const LEGACY_TOKEN_ENV_VAR = 'MEMORY_OS_MCP_TOKEN';
|
|
@@ -64,6 +65,10 @@ export async function run(args, io = defaultIo()) {
|
|
|
64
65
|
return 0;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
if (command === 'update' || command === '--update') {
|
|
69
|
+
return await updateCommand(args.slice(1), io);
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
if (command === 'doctor') {
|
|
68
73
|
return await doctorCommand(args.slice(1), io);
|
|
69
74
|
}
|
|
@@ -128,7 +133,8 @@ function defaultIo() {
|
|
|
128
133
|
stdin: process.stdin,
|
|
129
134
|
stdout: process.stdout,
|
|
130
135
|
stderr: process.stderr,
|
|
131
|
-
fetch: globalThis.fetch
|
|
136
|
+
fetch: globalThis.fetch,
|
|
137
|
+
spawn
|
|
132
138
|
};
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -137,6 +143,8 @@ function writeHelp(io) {
|
|
|
137
143
|
writeLine(io.stdout, `Fallback npm package: ${FALLBACK_PACKAGE_NAME}; legacy command alias: ${LEGACY_COMMAND_NAME}`);
|
|
138
144
|
writeLine(io.stdout, '');
|
|
139
145
|
writeLine(io.stdout, 'Usage:');
|
|
146
|
+
writeLine(io.stdout, ` ${COMMAND_NAME} update [--dry-run] [--json]`);
|
|
147
|
+
writeLine(io.stdout, ` ${COMMAND_NAME} --update [--dry-run] [--json]`);
|
|
140
148
|
writeLine(io.stdout, ` ${COMMAND_NAME} doctor [--base-url <https://api.example.com>] [--json]`);
|
|
141
149
|
writeLine(io.stdout, ` ${COMMAND_NAME} discovery show [--base-url <https://api.example.com>] [--json]`);
|
|
142
150
|
writeLine(io.stdout, ` ${COMMAND_NAME} setup [codex|cursor] [--url <https://api.example.com>] [--write|--yes] [--json]`);
|
|
@@ -162,6 +170,49 @@ function writeHelp(io) {
|
|
|
162
170
|
writeLine(io.stdout, '`login` and `token add` store credentials only in the user-scoped XMemo CLI config directory.');
|
|
163
171
|
}
|
|
164
172
|
|
|
173
|
+
async function updateCommand(args, io) {
|
|
174
|
+
const outputJson = hasFlag(args, '--json');
|
|
175
|
+
const dryRun = hasFlag(args, '--dry-run');
|
|
176
|
+
const npmCommand = npmExecutable();
|
|
177
|
+
const npmArgs = ['install', '-g', `${PACKAGE_NAME}@latest`];
|
|
178
|
+
const report = {
|
|
179
|
+
package: PACKAGE_NAME,
|
|
180
|
+
command: [npmCommand, ...npmArgs],
|
|
181
|
+
dryRun,
|
|
182
|
+
tokenSent: false,
|
|
183
|
+
projectFilesModified: false
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
if (dryRun) {
|
|
187
|
+
if (outputJson) {
|
|
188
|
+
writeLine(io.stdout, JSON.stringify(report, null, 2));
|
|
189
|
+
} else {
|
|
190
|
+
writeLine(io.stdout, `Update command: ${report.command.join(' ')}`);
|
|
191
|
+
writeLine(io.stdout, 'Dry run only; no changes made.');
|
|
192
|
+
}
|
|
193
|
+
return 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!outputJson) {
|
|
197
|
+
writeLine(io.stdout, `Updating ${PACKAGE_NAME} with: ${report.command.join(' ')}`);
|
|
198
|
+
}
|
|
199
|
+
const result = await runProcess(npmCommand, npmArgs, io, { stream: !outputJson });
|
|
200
|
+
report.exitCode = result.code;
|
|
201
|
+
report.completed = result.code === 0;
|
|
202
|
+
|
|
203
|
+
if (outputJson) {
|
|
204
|
+
writeLine(io.stdout, JSON.stringify(report, null, 2));
|
|
205
|
+
}
|
|
206
|
+
if (result.code !== 0) {
|
|
207
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
|
|
208
|
+
throw new UsageError(`Update failed: ${detail}`);
|
|
209
|
+
}
|
|
210
|
+
if (!outputJson) {
|
|
211
|
+
writeLine(io.stdout, `Update complete. Run \`${COMMAND_NAME} --version\` to confirm.`);
|
|
212
|
+
}
|
|
213
|
+
return 0;
|
|
214
|
+
}
|
|
215
|
+
|
|
165
216
|
async function doctorCommand(args, io) {
|
|
166
217
|
const baseUrl = normalizeBaseUrl(baseUrlOption(args, io.env));
|
|
167
218
|
const outputJson = hasFlag(args, '--json');
|
|
@@ -1876,6 +1927,42 @@ async function sleep(ms) {
|
|
|
1876
1927
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1877
1928
|
}
|
|
1878
1929
|
|
|
1930
|
+
|
|
1931
|
+
function npmExecutable() {
|
|
1932
|
+
return os.platform() === 'win32' ? 'npm.cmd' : 'npm';
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
|
|
1936
|
+
async function runProcess(command, args, io, { stream = true } = {}) {
|
|
1937
|
+
const spawnFn = io.spawn ?? spawn;
|
|
1938
|
+
return await new Promise((resolve, reject) => {
|
|
1939
|
+
const child = spawnFn(command, args, {
|
|
1940
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
1941
|
+
});
|
|
1942
|
+
let stdout = '';
|
|
1943
|
+
let stderr = '';
|
|
1944
|
+
child.stdout?.on('data', (chunk) => {
|
|
1945
|
+
const text = String(chunk);
|
|
1946
|
+
stdout += text;
|
|
1947
|
+
if (stream) {
|
|
1948
|
+
io.stdout.write(text);
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
child.stderr?.on('data', (chunk) => {
|
|
1952
|
+
const text = String(chunk);
|
|
1953
|
+
stderr += text;
|
|
1954
|
+
if (stream) {
|
|
1955
|
+
io.stderr.write(text);
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
child.on('error', reject);
|
|
1959
|
+
child.on('close', (code) => {
|
|
1960
|
+
resolve({ code: code ?? 0, stdout, stderr });
|
|
1961
|
+
});
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
|
|
1879
1966
|
async function readAll(stream) {
|
|
1880
1967
|
let content = '';
|
|
1881
1968
|
for await (const chunk of stream) {
|