@magclaw/cli-core 0.1.22
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 +7 -0
- package/bin/magclaw-daemon.js +7 -0
- package/bin/magclaw.js +7 -0
- package/package.json +22 -0
- package/src/cli.js +5056 -0
- package/src/list-renderer.js +146 -0
- package/src/mcp-bridge.js +612 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
const RESET = '\u001b[0m';
|
|
2
|
+
const BOLD = '\u001b[1m';
|
|
3
|
+
const DIM = '\u001b[2m';
|
|
4
|
+
const MAGCLAW = '\u001b[38;2;255;102;204m';
|
|
5
|
+
const GREEN = '\u001b[32m';
|
|
6
|
+
const YELLOW = '\u001b[33m';
|
|
7
|
+
const RED = '\u001b[31m';
|
|
8
|
+
const CYAN = '\u001b[36m';
|
|
9
|
+
|
|
10
|
+
const ANSI_PATTERN = /\u001b\[[0-9;]*m/g;
|
|
11
|
+
const BEIJING_OFFSET_MS = 8 * 60 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
function stripAnsi(value) {
|
|
14
|
+
return String(value || '').replace(ANSI_PATTERN, '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function characterWidth(character) {
|
|
18
|
+
const codePoint = character.codePointAt(0) || 0;
|
|
19
|
+
if (
|
|
20
|
+
(codePoint >= 0x1100 && codePoint <= 0x115f)
|
|
21
|
+
|| (codePoint >= 0x2e80 && codePoint <= 0xa4cf)
|
|
22
|
+
|| (codePoint >= 0xac00 && codePoint <= 0xd7a3)
|
|
23
|
+
|| (codePoint >= 0xf900 && codePoint <= 0xfaff)
|
|
24
|
+
|| (codePoint >= 0xfe10 && codePoint <= 0xfe19)
|
|
25
|
+
|| (codePoint >= 0xfe30 && codePoint <= 0xfe6f)
|
|
26
|
+
|| (codePoint >= 0xff00 && codePoint <= 0xff60)
|
|
27
|
+
|| (codePoint >= 0xffe0 && codePoint <= 0xffe6)
|
|
28
|
+
) {
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function visibleLength(value) {
|
|
35
|
+
return Array.from(stripAnsi(value)).reduce((width, character) => width + characterWidth(character), 0);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function colorize(enabled, code, value) {
|
|
39
|
+
return enabled ? `${code}${value}${RESET}` : value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function padVisible(value, width) {
|
|
43
|
+
const text = String(value ?? '');
|
|
44
|
+
return `${text}${' '.repeat(Math.max(0, width - visibleLength(text)))}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fallback(value, empty = '-') {
|
|
48
|
+
const text = String(value || '').trim();
|
|
49
|
+
return text || empty;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function statusLabel(profile, color) {
|
|
53
|
+
if (profile.running) return colorize(color, GREEN, 'RUNNING');
|
|
54
|
+
if (profile.service?.active) return colorize(color, YELLOW, 'SERVICE');
|
|
55
|
+
return colorize(color, RED, 'STOPPED');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function serviceLabel(profile) {
|
|
59
|
+
const mode = fallback(profile.service?.mode, 'foreground');
|
|
60
|
+
return profile.service?.active ? `${mode}: active` : `${mode}: inactive`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function shouldUseColor({ env = process.env, stream = process.stdout, flags = {} } = {}) {
|
|
64
|
+
if (flags.color === true || flags.color === 'true' || flags.color === 'always') return true;
|
|
65
|
+
if (flags.noColor || flags.color === 'false' || flags.color === 'never' || env.NO_COLOR) return false;
|
|
66
|
+
if (env.FORCE_COLOR) return true;
|
|
67
|
+
return Boolean(stream?.isTTY);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function formatBeijingTimestamp(value) {
|
|
71
|
+
const timestamp = Date.parse(String(value || ''));
|
|
72
|
+
if (!Number.isFinite(timestamp)) return '-';
|
|
73
|
+
const date = new Date(timestamp + BEIJING_OFFSET_MS);
|
|
74
|
+
const pad = (item) => String(item).padStart(2, '0');
|
|
75
|
+
return [
|
|
76
|
+
`${date.getUTCFullYear()}年${pad(date.getUTCMonth() + 1)}月${pad(date.getUTCDate())}日`,
|
|
77
|
+
`${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}`,
|
|
78
|
+
].join(' ');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function rowForProfile(profile, color) {
|
|
82
|
+
return {
|
|
83
|
+
status: statusLabel(profile, color),
|
|
84
|
+
profile: fallback(profile.profile),
|
|
85
|
+
computerName: fallback(profile.computerName || profile.name),
|
|
86
|
+
serverName: fallback(profile.serverName || profile.serverSlug || profile.workspaceId),
|
|
87
|
+
serverSlug: fallback(profile.serverSlug || profile.profile),
|
|
88
|
+
computerId: fallback(profile.computerId),
|
|
89
|
+
service: serviceLabel(profile),
|
|
90
|
+
updatedAt: formatBeijingTimestamp(profile.updatedAt || profile.createdAt),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function renderTable(rows, color) {
|
|
95
|
+
const headers = {
|
|
96
|
+
status: 'Status',
|
|
97
|
+
profile: 'Profile',
|
|
98
|
+
computerName: 'Computer Name',
|
|
99
|
+
serverName: 'Server Name',
|
|
100
|
+
serverSlug: 'Server Slug',
|
|
101
|
+
computerId: 'Computer ID',
|
|
102
|
+
service: 'Service',
|
|
103
|
+
updatedAt: 'Updated At (UTC+8)',
|
|
104
|
+
};
|
|
105
|
+
const keys = Object.keys(headers);
|
|
106
|
+
const widths = Object.fromEntries(keys.map((key) => [
|
|
107
|
+
key,
|
|
108
|
+
Math.max(visibleLength(headers[key]), ...rows.map((row) => visibleLength(row[key]))),
|
|
109
|
+
]));
|
|
110
|
+
const divider = `+-${keys.map((key) => '-'.repeat(widths[key])).join('-+-')}-+`;
|
|
111
|
+
const renderRow = (row) => `| ${keys.map((key) => padVisible(row[key], widths[key])).join(' | ')} |`;
|
|
112
|
+
return [
|
|
113
|
+
divider,
|
|
114
|
+
renderRow(Object.fromEntries(keys.map((key) => [key, colorize(color, BOLD, headers[key])]))),
|
|
115
|
+
divider,
|
|
116
|
+
...rows.map(renderRow),
|
|
117
|
+
divider,
|
|
118
|
+
].join('\n');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function renderListProfiles(payload = {}, options = {}) {
|
|
122
|
+
const color = Boolean(options.color);
|
|
123
|
+
const profiles = Array.isArray(payload.profiles) ? payload.profiles : [];
|
|
124
|
+
const runningCount = profiles.filter((profile) => profile.running).length;
|
|
125
|
+
const machineTokenCount = profiles.filter((profile) => profile.hasMachineToken).length;
|
|
126
|
+
const pairTokenCount = profiles.filter((profile) => profile.hasPairToken).length;
|
|
127
|
+
const title = colorize(color, `${BOLD}${MAGCLAW}`, 'MagClaw Computers');
|
|
128
|
+
const summary = [
|
|
129
|
+
`${colorize(color, MAGCLAW, 'Profiles')}: ${profiles.length}`,
|
|
130
|
+
`${colorize(color, GREEN, 'Running')}: ${runningCount}`,
|
|
131
|
+
`${colorize(color, CYAN, 'Machine Tokens')}: ${machineTokenCount}`,
|
|
132
|
+
`${colorize(color, YELLOW, 'Pair Tokens')}: ${pairTokenCount}`,
|
|
133
|
+
].join(' ');
|
|
134
|
+
const lines = [
|
|
135
|
+
title,
|
|
136
|
+
summary,
|
|
137
|
+
`${colorize(color, DIM, 'Root')}: ${payload.root || '-'}`,
|
|
138
|
+
'',
|
|
139
|
+
];
|
|
140
|
+
if (!profiles.length) {
|
|
141
|
+
lines.push('No saved Computers found.');
|
|
142
|
+
return `${lines.join('\n')}\n`;
|
|
143
|
+
}
|
|
144
|
+
lines.push(renderTable(profiles.map((profile) => rowForProfile(profile, color)), color));
|
|
145
|
+
return `${lines.join('\n')}\n`;
|
|
146
|
+
}
|