@xmemo/client 0.4.155 → 0.4.156

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.
@@ -0,0 +1,190 @@
1
+ import {
2
+ hasFlag,
3
+ optionValue,
4
+ parsePositiveInteger,
5
+ stringValue
6
+ } from '../args.js';
7
+ import { baseUrlOption } from '../base-url.js';
8
+ import {
9
+ DEFAULT_PROXY_PORT
10
+ } from '../constants.js';
11
+ import {
12
+ buildSetupPlan,
13
+ ensureDiscoveryService
14
+ } from '../discovery.js';
15
+ import { UsageError } from '../errors.js';
16
+ import {
17
+ endpointUrl,
18
+ fetchJson,
19
+ normalizeBaseUrl
20
+ } from '../http.js';
21
+ import { writeLine } from '../io.js';
22
+ import {
23
+ MCP_CLIENTS,
24
+ supportedMcpClients
25
+ } from '../mcp/clients.js';
26
+ import { mergeCopilotMcpConfig } from '../mcp/copilot-proxy.js';
27
+ import { detectClient } from '../mcp/detect.js';
28
+ import {
29
+ agentIdentity,
30
+ envReferenceIdentity
31
+ } from '../mcp/identity.js';
32
+ import {
33
+ confirmProfileInstall,
34
+ defaultProfileTarget,
35
+ profileClientConfig,
36
+ profileInstallResult
37
+ } from '../profile.js';
38
+ import {
39
+ clientSetupPlan,
40
+ copilotSetupPlan,
41
+ normalizeSetupClientId,
42
+ positionalClientArg,
43
+ supportedSetupClientIds,
44
+ writeSetupSummary
45
+ } from '../setup.js';
46
+
47
+ export async function setupCommand(args, io) {
48
+ const positionalClientId = positionalClientArg(args, MCP_CLIENTS);
49
+ const optionArgs = positionalClientId ? args.slice(1) : args;
50
+ const baseUrl = normalizeBaseUrl(baseUrlOption(optionArgs, io.env));
51
+ const outputJson = hasFlag(optionArgs, '--json');
52
+ const shortClientSetup = Boolean(positionalClientId);
53
+ const setupAll = hasFlag(optionArgs, '--all');
54
+
55
+ let clientId = null;
56
+ try {
57
+ clientId = normalizeSetupClientId(positionalClientId ?? optionValue(optionArgs, '--client'), MCP_CLIENTS);
58
+ } catch (error) {
59
+ if (!setupAll) {
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ if (setupAll && clientId) {
65
+ throw new UsageError('Cannot specify both --all and a specific client.');
66
+ }
67
+
68
+ const dryRun = hasFlag(optionArgs, '--dry-run') || hasFlag(optionArgs, '--preview');
69
+ const writeConfig = !dryRun && (hasFlag(optionArgs, '--write') || hasFlag(optionArgs, '--yes') || shortClientSetup || (setupAll && (hasFlag(optionArgs, '--write') || hasFlag(optionArgs, '--yes'))));
70
+ const timeoutMs = parsePositiveInteger(optionValue(optionArgs, '--timeout-ms') ?? '5000', '--timeout-ms');
71
+
72
+ if (writeConfig && !clientId && !setupAll) {
73
+ throw new UsageError(`Setup --write requires --client <${supportedSetupClientIds(MCP_CLIENTS).join('|')}> or --all so the CLI never writes broad config implicitly.`);
74
+ }
75
+
76
+ const discoveryUrl = endpointUrl(baseUrl, '/.well-known/memory-os.json');
77
+ const discovery = await fetchJson(discoveryUrl, timeoutMs, io);
78
+ ensureDiscoveryService(discovery, discoveryUrl);
79
+
80
+ const statusUrl = stringValue(discovery, ['urls', 'onboarding_status'])
81
+ ?? stringValue(discovery, ['onboarding_status_url'])
82
+ ?? endpointUrl(baseUrl, '/v1/onboarding/status');
83
+ const status = await fetchJson(statusUrl, timeoutMs, io);
84
+ const setupPlan = buildSetupPlan({
85
+ baseUrl,
86
+ discoveryUrl,
87
+ statusUrl,
88
+ discovery,
89
+ status,
90
+ localClients: supportedMcpClients()
91
+ });
92
+
93
+ if (setupAll) {
94
+ setupPlan.detectedClients = [];
95
+ const scanIds = ['codex', 'cursor', 'copilot-cli', 'gemini-cli', 'antigravity', 'antigravity-ide', 'antigravity2', 'antigravity-cli', 'windsurf', 'cline', 'continue', 'claude-desktop', 'qwen', 'opencode', 'trae', 'trae-solo'];
96
+ for (const scanId of scanIds) {
97
+ const detection = await detectClient(scanId, io.env, MCP_CLIENTS);
98
+ if (detection.detected) {
99
+ let clientPlan;
100
+ if (scanId === 'copilot-cli') {
101
+ const proxyPort = parsePositiveInteger(optionValue(optionArgs, '--port') ?? String(DEFAULT_PROXY_PORT), '--port');
102
+ clientPlan = copilotSetupPlan(setupPlan.mcpUrl, proxyPort, io.env);
103
+ clientPlan.configPath = detection.path;
104
+ if (writeConfig) {
105
+ await mergeCopilotMcpConfig(clientPlan.configPath, clientPlan.proxyUrl);
106
+ clientPlan.written = true;
107
+ }
108
+ } else {
109
+ const client = MCP_CLIENTS.get(scanId);
110
+ const identity = writeConfig ? await agentIdentity(scanId, io.env) : envReferenceIdentity(scanId);
111
+ clientPlan = clientSetupPlan(scanId, client, setupPlan.mcpUrl, io.env, identity);
112
+ clientPlan.configPath = detection.path;
113
+ if (writeConfig) {
114
+ await client.writeConfig(clientPlan.configPath, setupPlan.mcpUrl, identity);
115
+ clientPlan.written = true;
116
+ if (profileClientConfig(scanId)) {
117
+ const installProfile = hasFlag(optionArgs, '--yes') || hasFlag(optionArgs, '--profile');
118
+ if (installProfile) {
119
+ const profileTarget = defaultProfileTarget(scanId, io.env);
120
+ const profileResult = await profileInstallResult(scanId, profileTarget, { write: true });
121
+ clientPlan.behaviorProfile = profileResult;
122
+ if (scanId === 'codex') {
123
+ clientPlan.codexProfile = profileResult;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ setupPlan.detectedClients.push(clientPlan);
130
+ }
131
+ }
132
+ } else if (clientId) {
133
+ if (clientId === 'copilot-cli') {
134
+ const proxyPort = parsePositiveInteger(optionValue(optionArgs, '--port') ?? String(DEFAULT_PROXY_PORT), '--port');
135
+ setupPlan.selectedClient = copilotSetupPlan(setupPlan.mcpUrl, proxyPort, io.env);
136
+ if (writeConfig) {
137
+ await mergeCopilotMcpConfig(setupPlan.selectedClient.configPath, setupPlan.selectedClient.proxyUrl);
138
+ setupPlan.selectedClient.written = true;
139
+ }
140
+ } else {
141
+ const client = MCP_CLIENTS.get(clientId);
142
+ if (!client) {
143
+ throw new UsageError(`Unsupported MCP client: ${clientId}. Supported clients: ${supportedSetupClientIds(MCP_CLIENTS).join(', ')}.`);
144
+ }
145
+
146
+ const identity = writeConfig ? await agentIdentity(clientId, io.env) : envReferenceIdentity(clientId);
147
+ setupPlan.selectedClient = clientSetupPlan(clientId, client, setupPlan.mcpUrl, io.env, identity);
148
+ if (writeConfig) {
149
+ await client.writeConfig(setupPlan.selectedClient.configPath, setupPlan.mcpUrl, identity);
150
+ setupPlan.selectedClient.written = true;
151
+ }
152
+
153
+ if (shortClientSetup && profileClientConfig(clientId)) {
154
+ const profileTarget = optionValue(optionArgs, '--profile-target')
155
+ ?? optionValue(optionArgs, '--target')
156
+ ?? defaultProfileTarget(clientId, io.env);
157
+ let installProfile = false;
158
+ let prompted = false;
159
+ let skipped = false;
160
+ if (hasFlag(optionArgs, '--no-profile')) {
161
+ skipped = true;
162
+ } else if (dryRun) {
163
+ installProfile = false;
164
+ } else if (writeConfig) {
165
+ installProfile = outputJson || hasFlag(optionArgs, '--yes') || hasFlag(optionArgs, '--profile');
166
+ if (!installProfile && !outputJson) {
167
+ prompted = true;
168
+ installProfile = await confirmProfileInstall(clientId, profileTarget, io);
169
+ }
170
+ }
171
+ const profileResult = await profileInstallResult(clientId, profileTarget, { write: installProfile });
172
+ profileResult.prompted = prompted;
173
+ profileResult.accepted = installProfile;
174
+ profileResult.skipped = skipped;
175
+ setupPlan.selectedClient.behaviorProfile = profileResult;
176
+ if (clientId === 'codex') {
177
+ setupPlan.selectedClient.codexProfile = profileResult;
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ if (outputJson) {
184
+ writeLine(io.stdout, JSON.stringify(setupPlan, null, 2));
185
+ return 0;
186
+ }
187
+
188
+ writeSetupSummary(setupPlan, io);
189
+ return 0;
190
+ }
@@ -0,0 +1,57 @@
1
+ import { hasFlag } from '../args.js';
2
+ import {
3
+ COMMAND_NAME,
4
+ PACKAGE_NAME
5
+ } from '../constants.js';
6
+ import { UsageError } from '../errors.js';
7
+ import { writeLine } from '../io.js';
8
+ import {
9
+ npmExecutable,
10
+ runProcess
11
+ } from '../runtime.js';
12
+
13
+ export async function updateCommand(args, io) {
14
+ const outputJson = hasFlag(args, '--json');
15
+ const dryRun = hasFlag(args, '--dry-run');
16
+ const npmCommand = npmExecutable();
17
+ const npmArgs = ['install', '-g', `${PACKAGE_NAME}@latest`];
18
+ const report = {
19
+ package: PACKAGE_NAME,
20
+ command: [npmCommand, ...npmArgs],
21
+ dryRun,
22
+ tokenSent: false,
23
+ projectFilesModified: false
24
+ };
25
+
26
+ const commandToDisplay = report.command.map(c => c === 'npm.cmd' ? 'npm' : c).join(' ');
27
+
28
+ if (dryRun) {
29
+ if (outputJson) {
30
+ writeLine(io.stdout, JSON.stringify(report, null, 2));
31
+ } else {
32
+ writeLine(io.stdout, `Update command: ${commandToDisplay}`);
33
+ writeLine(io.stdout, 'Dry run only; no changes made.');
34
+ }
35
+ return 0;
36
+ }
37
+
38
+ if (!outputJson) {
39
+ writeLine(io.stdout, `Updating ${PACKAGE_NAME} to the latest version...`);
40
+ writeLine(io.stdout, `Running: ${commandToDisplay}`);
41
+ }
42
+ const result = await runProcess(npmCommand, npmArgs, io, { stream: !outputJson });
43
+ report.exitCode = result.code;
44
+ report.completed = result.code === 0;
45
+
46
+ if (outputJson) {
47
+ writeLine(io.stdout, JSON.stringify(report, null, 2));
48
+ }
49
+ if (result.code !== 0) {
50
+ const detail = result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`;
51
+ throw new UsageError(`Update failed: ${detail}`);
52
+ }
53
+ if (!outputJson) {
54
+ writeLine(io.stdout, `Update complete. Run \`${COMMAND_NAME} --version\` to confirm.`);
55
+ }
56
+ return 0;
57
+ }
@@ -0,0 +1,32 @@
1
+ export const PRODUCT_NAME = 'XMemo';
2
+ export const PACKAGE_NAME = '@xmemo/client';
3
+ export const FALLBACK_PACKAGE_NAME = '@yonro/xmemo-client';
4
+ export const COMMAND_NAME = 'xmemo';
5
+ export const LEGACY_COMMAND_NAME = 'memory-os';
6
+ export { CLI_VERSION } from './version.js';
7
+ export const DEFAULT_SERVICE_URL = 'https://xmemo.dev';
8
+ export const TOKEN_ENV_VAR = 'XMEMO_KEY';
9
+ export const LEGACY_TOKEN_ENV_VAR = 'MEMORY_OS_MCP_TOKEN';
10
+ export const AGENT_ID_ENV_VAR = 'XMEMO_AGENT_ID';
11
+ export const AGENT_INSTANCE_ENV_VAR = 'XMEMO_AGENT_INSTANCE_ID';
12
+ export const AGENT_ID_HEADER = 'X-Memory-OS-Agent-ID';
13
+ export const AGENT_INSTANCE_HEADER = 'X-Memory-OS-Agent-Instance-ID';
14
+ export const MCP_SERVER_NAME = 'XMemo';
15
+ export const LEGACY_MCP_SERVER_NAMES = ['memory_os', 'memory-os'];
16
+ export const CODEX_PROFILE_TARGET = 'AGENTS.md';
17
+ export const CODEX_PROFILE_MARKER_START = '<!-- memory-os:codex-profile:start -->';
18
+ export const CODEX_PROFILE_MARKER_END = '<!-- memory-os:codex-profile:end -->';
19
+ export const CLIENT_PROFILE_TARGETS = {
20
+ cursor: '.cursor/rules/xmemo-memory.md',
21
+ 'gemini-cli': 'GEMINI.md',
22
+ antigravity: 'GEMINI.md',
23
+ trae: '.trae/rules/xmemo-memory.md',
24
+ 'trae-solo': '.trae/rules/xmemo-memory.md'
25
+ };
26
+ export const CLIENT_PROFILE_MARKER_START = '<!-- xmemo:profile:start -->';
27
+ export const CLIENT_PROFILE_MARKER_END = '<!-- xmemo:profile:end -->';
28
+ export const PROFILE_MARKER_PREFIX = 'memory-os:memory-profile';
29
+ export const DEVICE_LOGIN_START_PATH = '/api/v1/auth/device/start';
30
+ export const DEVICE_LOGIN_TOKEN_PATH = '/api/v1/auth/device/token';
31
+ export const DEFAULT_PROXY_HOST = '127.0.0.1';
32
+ export const DEFAULT_PROXY_PORT = 8765;
@@ -0,0 +1,102 @@
1
+ import { arrayValue, booleanValue, stringValue } from './args.js';
2
+ import { TOKEN_ENV_VAR } from './constants.js';
3
+ import { UsageError } from './errors.js';
4
+ import { endpointUrl, fetchJson } from './http.js';
5
+ import { isPlainObject } from './runtime.js';
6
+
7
+ export function ensureDiscoveryService(discovery, discoveryUrl) {
8
+ const service = stringValue(discovery, ['service']);
9
+ if (service && service !== 'memory-os') {
10
+ throw new UsageError(`Discovery document at ${discoveryUrl} is for '${service}', not 'memory-os'.`);
11
+ }
12
+ }
13
+
14
+ export function buildSetupPlan({ baseUrl, discoveryUrl, statusUrl, discovery, status, localClients }) {
15
+ const apiBase = stringValue(discovery, ['urls', 'api_base'])
16
+ ?? stringValue(discovery, ['api_base_url'])
17
+ ?? baseUrl;
18
+ const mcpUrl = stringValue(discovery, ['urls', 'mcp'])
19
+ ?? stringValue(discovery, ['mcp_url'])
20
+ ?? endpointUrl(apiBase, '/mcp');
21
+ const tokenPortalUrl = stringValue(discovery, ['urls', 'token_portal'])
22
+ ?? stringValue(discovery, ['token_portal_url'])
23
+ ?? stringValue(status, ['requirements', 'token_portal_url']);
24
+ const tokenEnvVar = stringValue(discovery, ['auth', 'token_env_var'])
25
+ ?? stringValue(status, ['requirements', 'token_env_var'])
26
+ ?? TOKEN_ENV_VAR;
27
+
28
+ return {
29
+ schemaVersion: '1.0',
30
+ baseUrl,
31
+ discoveryUrl,
32
+ statusUrl,
33
+ apiBase,
34
+ mcpUrl,
35
+ guideUrl: stringValue(discovery, ['urls', 'guide']) ?? endpointUrl(apiBase, '/guide'),
36
+ docsUrl: stringValue(discovery, ['urls', 'docs']),
37
+ tokenPortalUrl,
38
+ tokenEnvVar,
39
+ onboardingReady: booleanValue(status, ['ready']),
40
+ supportedClients: discoveryMcpClients(discovery),
41
+ localClients,
42
+ privacy: {
43
+ telemetry: false,
44
+ tokenSent: false,
45
+ tokenEmbeddedInConfig: false
46
+ },
47
+ boundaries: {
48
+ clientAllowed: arrayValue(discovery, ['agent_boundary', 'client_allowed'])
49
+ ?? arrayValue(status, ['agent_boundary', 'client_allowed'])
50
+ ?? [],
51
+ adminRequired: arrayValue(discovery, ['agent_boundary', 'admin_required'])
52
+ ?? arrayValue(status, ['agent_boundary', 'admin_required'])
53
+ ?? []
54
+ }
55
+ };
56
+ }
57
+
58
+ export async function bestEffortRootVersion(discovery, timeoutMs, io) {
59
+ const rootDiscoveryUrl = stringValue(discovery, ['urls', 'root_discovery']);
60
+ if (!rootDiscoveryUrl) {
61
+ return {};
62
+ }
63
+ try {
64
+ const rootDiscovery = await fetchJson(rootDiscoveryUrl, timeoutMs, io);
65
+ return { version: stringValue(rootDiscovery, ['version']) ?? undefined };
66
+ } catch (error) {
67
+ return { error: error.message };
68
+ }
69
+ }
70
+
71
+ export function discoveryMcpUrl(discovery, baseUrl) {
72
+ return stringValue(discovery, ['api', 'mcp', 'url'])
73
+ ?? stringValue(discovery, ['urls', 'mcp'])
74
+ ?? endpointUrl(baseUrl, '/mcp');
75
+ }
76
+
77
+ export function agentDiscoveryClientIds(discovery) {
78
+ const clients = Array.isArray(discovery?.clients) ? discovery.clients : [];
79
+ const ids = clients
80
+ .filter((client) => isPlainObject(client) && typeof client.id === 'string')
81
+ .map((client) => client.id);
82
+ if (ids.length > 0) {
83
+ return ids;
84
+ }
85
+ const supported = arrayValue(discovery, ['supported_clients']);
86
+ return supported ?? [];
87
+ }
88
+
89
+ export function discoveryMcpClients(discovery) {
90
+ const clients = discovery?.clients?.mcp;
91
+ if (!Array.isArray(clients)) {
92
+ return [];
93
+ }
94
+
95
+ return clients
96
+ .filter((client) => isPlainObject(client) && typeof client.id === 'string')
97
+ .map((client) => ({
98
+ id: client.id,
99
+ configEndpoint: typeof client.config_endpoint === 'string' ? client.config_endpoint : null
100
+ }));
101
+ }
102
+
package/src/env.js ADDED
@@ -0,0 +1,81 @@
1
+ import { hasFlag, optionValue } from './args.js';
2
+ import { baseUrlOption } from './base-url.js';
3
+ import {
4
+ AGENT_ID_ENV_VAR,
5
+ AGENT_INSTANCE_ENV_VAR,
6
+ COMMAND_NAME,
7
+ PRODUCT_NAME,
8
+ TOKEN_ENV_VAR
9
+ } from './constants.js';
10
+ import { UsageError } from './errors.js';
11
+ import { normalizeBaseUrl } from './http.js';
12
+ import { writeLine } from './io.js';
13
+
14
+ export function envCommand(args, io) {
15
+ const subcommand = args[0] ?? 'help';
16
+ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
17
+ writeLine(io.stdout, 'Env commands:');
18
+ writeLine(io.stdout, ` ${COMMAND_NAME} env example [--shell bash|powershell|cmd] [--base-url <url>] [--json]`);
19
+ return 0;
20
+ }
21
+ if (subcommand !== 'example') {
22
+ throw new UsageError(`Unknown env command: ${subcommand}`);
23
+ }
24
+
25
+ const baseUrl = normalizeBaseUrl(baseUrlOption(args.slice(1), io.env));
26
+ const outputJson = hasFlag(args, '--json');
27
+ const shell = optionValue(args, '--shell') ?? (process.platform === 'win32' ? 'powershell' : 'bash');
28
+ const placeholder = '<paste-token-from-your-secret-store>';
29
+ const payload = {
30
+ XMEMO_URL: baseUrl,
31
+ XMEMO_BASE_URL: baseUrl,
32
+ MEMORY_OS_URL: baseUrl,
33
+ MEMORY_OS_BASE_URL: baseUrl,
34
+ [TOKEN_ENV_VAR]: placeholder,
35
+ [AGENT_ID_ENV_VAR]: '<agent-family>',
36
+ [AGENT_INSTANCE_ENV_VAR]: '<stable-random-id-for-this-local-agent>'
37
+ };
38
+
39
+ if (outputJson) {
40
+ writeLine(io.stdout, JSON.stringify(payload, null, 2));
41
+ return 0;
42
+ }
43
+
44
+ if (shell === 'powershell') {
45
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('XMEMO_URL', '${baseUrl}', 'User')`);
46
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('XMEMO_BASE_URL', '${baseUrl}', 'User')`);
47
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('MEMORY_OS_URL', '${baseUrl}', 'User')`);
48
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('MEMORY_OS_BASE_URL', '${baseUrl}', 'User')`);
49
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('${TOKEN_ENV_VAR}', '${placeholder}', 'User')`);
50
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('${AGENT_ID_ENV_VAR}', '<agent-family>', 'User')`);
51
+ writeLine(io.stdout, `[Environment]::SetEnvironmentVariable('${AGENT_INSTANCE_ENV_VAR}', '<stable-random-id-for-this-local-agent>', 'User')`);
52
+ } else if (shell === 'cmd') {
53
+ writeLine(io.stdout, `setx XMEMO_URL "${baseUrl}"`);
54
+ writeLine(io.stdout, `setx XMEMO_BASE_URL "${baseUrl}"`);
55
+ writeLine(io.stdout, `setx MEMORY_OS_URL "${baseUrl}"`);
56
+ writeLine(io.stdout, `setx MEMORY_OS_BASE_URL "${baseUrl}"`);
57
+ writeLine(io.stdout, `setx ${TOKEN_ENV_VAR} "${placeholder}"`);
58
+ writeLine(io.stdout, `setx ${AGENT_ID_ENV_VAR} "<agent-family>"`);
59
+ writeLine(io.stdout, `setx ${AGENT_INSTANCE_ENV_VAR} "<stable-random-id-for-this-local-agent>"`);
60
+ } else {
61
+ writeLine(io.stdout, `export XMEMO_URL="${baseUrl}"`);
62
+ writeLine(io.stdout, `export XMEMO_BASE_URL="${baseUrl}"`);
63
+ writeLine(io.stdout, `export MEMORY_OS_URL="${baseUrl}"`);
64
+ writeLine(io.stdout, `export MEMORY_OS_BASE_URL="${baseUrl}"`);
65
+ writeLine(io.stdout, `export ${TOKEN_ENV_VAR}="${placeholder}"`);
66
+ writeLine(io.stdout, `export ${AGENT_ID_ENV_VAR}="<agent-family>"`);
67
+ writeLine(io.stdout, `export ${AGENT_INSTANCE_ENV_VAR}="<stable-random-id-for-this-local-agent>"`);
68
+ }
69
+ return 0;
70
+ }
71
+
72
+ export function writePrivacy(io) {
73
+ writeLine(io.stdout, `${PRODUCT_NAME} CLI privacy and security defaults:`);
74
+ writeLine(io.stdout, '- No telemetry or analytics.');
75
+ writeLine(io.stdout, '- `status` does not send tokens.');
76
+ writeLine(io.stdout, `- MCP configs reference ${TOKEN_ENV_VAR}; token values are not embedded.`);
77
+ writeLine(io.stdout, `- Agent instance IDs are non-secret and stored in user-scoped config outside git.`);
78
+ writeLine(io.stdout, '- `login` and `token add` store credentials in the user-scoped XMemo CLI config directory.');
79
+ writeLine(io.stdout, '- Legacy `token set` plaintext storage requires explicit --allow-plaintext.');
80
+ writeLine(io.stdout, '- npm publishing is restricted by package.json files whitelist.');
81
+ }
package/src/errors.js ADDED
@@ -0,0 +1,6 @@
1
+ export class UsageError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = 'UsageError';
5
+ }
6
+ }
package/src/help.js ADDED
@@ -0,0 +1,58 @@
1
+ import {
2
+ CLI_VERSION,
3
+ COMMAND_NAME,
4
+ LEGACY_COMMAND_NAME,
5
+ PACKAGE_NAME,
6
+ PRODUCT_NAME
7
+ } from './constants.js';
8
+ import { writeLine } from './io.js';
9
+
10
+ export function writeHelp(io) {
11
+ writeLine(io.stdout, `======================================================================`);
12
+ writeLine(io.stdout, ` 🧠 ${PRODUCT_NAME} CLI (Version ${CLI_VERSION}) — Cloud Memory Orchestration Utility`);
13
+ writeLine(io.stdout, `======================================================================`);
14
+ writeLine(io.stdout, `Official package: ${PACKAGE_NAME} | Legacy command: ${LEGACY_COMMAND_NAME}`);
15
+ writeLine(io.stdout, '');
16
+ writeLine(io.stdout, '💡 CORE ONBOARDING & SETUP COMMANDS:');
17
+ writeLine(io.stdout, ` ${COMMAND_NAME} setup --all [--write] [--profile]`);
18
+ writeLine(io.stdout, ` Auto-detects all local client installations (Cursor, VS Code, Continue, Trae, etc.).`);
19
+ writeLine(io.stdout, ` Merges XMemo MCP configs. Pass --profile to auto-inject workspace prompt rules.`);
20
+ writeLine(io.stdout, ` *Dry-run by default unless --write (or --yes/-y) is specified for safety.*`);
21
+ writeLine(io.stdout, '');
22
+ writeLine(io.stdout, ` ${COMMAND_NAME} setup <client-id> [--url <url>] [--no-profile] [--json]`);
23
+ writeLine(io.stdout, ` Runs interactive setup wizard for a single client (e.g. cursor, gemini, antigravity).`);
24
+ writeLine(io.stdout, ` Detects active workspace to auto-inject project-scoped instruction rules.`);
25
+ writeLine(io.stdout, '');
26
+ writeLine(io.stdout, ` ${COMMAND_NAME} login [--from-stdin] [--base-url <url>]`);
27
+ writeLine(io.stdout, ` Starts secure OAuth2 browser-based device login flow to register the CLI.`);
28
+ writeLine(io.stdout, '');
29
+ writeLine(io.stdout, '🛡️ DIAGNOSTICS & SYSTEM AUDIT:');
30
+ writeLine(io.stdout, ` ${COMMAND_NAME} doctor [--base-url <url>] [--json]`);
31
+ writeLine(io.stdout, ` Performs structural diagnostics (Node version, Cloud connectivity, API compatibility, security).`);
32
+ writeLine(io.stdout, '');
33
+ writeLine(io.stdout, ` ${COMMAND_NAME} status [--url <url>] [--json]`);
34
+ writeLine(io.stdout, ` Probes and audits XMemo core service endpoints, readiness states, and network health.`);
35
+ writeLine(io.stdout, '');
36
+ writeLine(io.stdout, '📋 MCP & CREDENTIAL MANAGEMENT:');
37
+ writeLine(io.stdout, ` ${COMMAND_NAME} mcp list`);
38
+ writeLine(io.stdout, ` Lists all natively supported client integrations and configurations.`);
39
+ writeLine(io.stdout, '');
40
+ writeLine(io.stdout, ` ${COMMAND_NAME} mcp config --client <client-id> [--base-url <url>] [--json]`);
41
+ writeLine(io.stdout, ` Generates and outputs raw MCP config snippet templates without writing to files.`);
42
+ writeLine(io.stdout, '');
43
+ writeLine(io.stdout, ` ${COMMAND_NAME} mcp add <client-id> [--write] [--config <path>]`);
44
+ writeLine(io.stdout, ` Directly adds XMemo MCP server config snippet to the specified client settings file.`);
45
+ writeLine(io.stdout, '');
46
+ writeLine(io.stdout, ` ${COMMAND_NAME} profile install <client-id> [--target <path>] [--dry-run]`);
47
+ writeLine(io.stdout, ` Injects/updates instruction rules prompt in target workspace rules files (Cursor/Gemini).`);
48
+ writeLine(io.stdout, '');
49
+ writeLine(io.stdout, ` ${COMMAND_NAME} token status [--verify] | ${COMMAND_NAME} token add --from-stdin`);
50
+ writeLine(io.stdout, ` Checks local static credential states or manually saves XMEMO_KEY for key-auth fallbacks.`);
51
+ writeLine(io.stdout, '');
52
+ writeLine(io.stdout, '🔐 SECURITY & PRIVACY BY DESIGN:');
53
+ writeLine(io.stdout, ' - ZERO Telemetry: We never collect private workspace data or usage metrics.');
54
+ writeLine(io.stdout, ' - Git Protection: API tokens are kept securely in system environment variables (XMEMO_KEY)');
55
+ writeLine(io.stdout, ' or in user-scoped credentials.json file. They are never written to project configs.');
56
+ writeLine(io.stdout, ' - AST Merge Safety: Config writes only touch and append the XMemo keys, preserving all other servers.');
57
+ writeLine(io.stdout, '======================================================================');
58
+ }