@meet-im/lxcli 0.0.1

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.
Files changed (90) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +3 -0
  3. package/bin/lxcli.js +2 -0
  4. package/dist/commands/auth.d.ts +2 -0
  5. package/dist/commands/auth.js +45 -0
  6. package/dist/commands/config.d.ts +2 -0
  7. package/dist/commands/config.js +28 -0
  8. package/dist/commands/generate-skills.d.ts +2 -0
  9. package/dist/commands/generate-skills.js +283 -0
  10. package/dist/commands/index.d.ts +9 -0
  11. package/dist/commands/index.js +43 -0
  12. package/dist/commands/schema.d.ts +2 -0
  13. package/dist/commands/schema.js +37 -0
  14. package/dist/commands/services/kb/config.d.ts +2 -0
  15. package/dist/commands/services/kb/config.js +32 -0
  16. package/dist/commands/services/kb/descriptions.d.ts +4 -0
  17. package/dist/commands/services/kb/descriptions.js +24 -0
  18. package/dist/commands/services/kb/handler.d.ts +12 -0
  19. package/dist/commands/services/kb/handler.js +179 -0
  20. package/dist/commands/services/kb/index.d.ts +3 -0
  21. package/dist/commands/services/kb/index.js +119 -0
  22. package/dist/commands/services/kb/params.d.ts +8 -0
  23. package/dist/commands/services/kb/params.js +73 -0
  24. package/dist/commands/services/kb/rpc.d.ts +1 -0
  25. package/dist/commands/services/kb/rpc.js +35 -0
  26. package/dist/commands/services/kb/types.d.ts +97 -0
  27. package/dist/commands/services/kb/types.js +2 -0
  28. package/dist/commands/services/kb/utils.d.ts +1 -0
  29. package/dist/commands/services/kb/utils.js +4 -0
  30. package/dist/commands/services/meet/api.d.ts +13 -0
  31. package/dist/commands/services/meet/api.js +38 -0
  32. package/dist/commands/services/meet/config.d.ts +4 -0
  33. package/dist/commands/services/meet/config.js +16 -0
  34. package/dist/commands/services/meet/descriptions.d.ts +3 -0
  35. package/dist/commands/services/meet/descriptions.js +7 -0
  36. package/dist/commands/services/meet/handler.d.ts +25 -0
  37. package/dist/commands/services/meet/handler.js +50 -0
  38. package/dist/commands/services/meet/index.d.ts +3 -0
  39. package/dist/commands/services/meet/index.js +43 -0
  40. package/dist/commands/services/meet/types.d.ts +65 -0
  41. package/dist/commands/services/meet/types.js +2 -0
  42. package/dist/commands/services/meetbot/api.d.ts +37 -0
  43. package/dist/commands/services/meetbot/api.js +130 -0
  44. package/dist/commands/services/meetbot/config.d.ts +4 -0
  45. package/dist/commands/services/meetbot/config.js +21 -0
  46. package/dist/commands/services/meetbot/descriptions.d.ts +3 -0
  47. package/dist/commands/services/meetbot/descriptions.js +10 -0
  48. package/dist/commands/services/meetbot/handler.d.ts +42 -0
  49. package/dist/commands/services/meetbot/handler.js +83 -0
  50. package/dist/commands/services/meetbot/index.d.ts +3 -0
  51. package/dist/commands/services/meetbot/index.js +74 -0
  52. package/dist/commands/services/meetbot/types.d.ts +101 -0
  53. package/dist/commands/services/meetbot/types.js +2 -0
  54. package/dist/commands/services/meetbot/utils.d.ts +28 -0
  55. package/dist/commands/services/meetbot/utils.js +78 -0
  56. package/dist/commands/services.d.ts +2 -0
  57. package/dist/commands/services.js +50 -0
  58. package/dist/commands/tenants.d.ts +2 -0
  59. package/dist/commands/tenants.js +46 -0
  60. package/dist/commands/upgrade.d.ts +2 -0
  61. package/dist/commands/upgrade.js +38 -0
  62. package/dist/commands/users.d.ts +2 -0
  63. package/dist/commands/users.js +182 -0
  64. package/dist/core/auth.d.ts +26 -0
  65. package/dist/core/auth.js +75 -0
  66. package/dist/core/config.d.ts +76 -0
  67. package/dist/core/config.js +255 -0
  68. package/dist/core/error.d.ts +128 -0
  69. package/dist/core/error.js +85 -0
  70. package/dist/core/help.d.ts +6 -0
  71. package/dist/core/help.js +15 -0
  72. package/dist/core/http.d.ts +28 -0
  73. package/dist/core/http.js +140 -0
  74. package/dist/core/logger.d.ts +24 -0
  75. package/dist/core/logger.js +53 -0
  76. package/dist/core/reserved.d.ts +13 -0
  77. package/dist/core/reserved.js +28 -0
  78. package/dist/core/tenants.d.ts +36 -0
  79. package/dist/core/tenants.js +50 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.js +152 -0
  82. package/dist/types/index.d.ts +59 -0
  83. package/dist/types/index.js +5 -0
  84. package/dist/utils/index.d.ts +20 -0
  85. package/dist/utils/index.js +133 -0
  86. package/dist/utils/loose-params.d.ts +9 -0
  87. package/dist/utils/loose-params.js +274 -0
  88. package/dist/utils/table.d.ts +4 -0
  89. package/dist/utils/table.js +25 -0
  90. package/package.json +46 -0
@@ -0,0 +1,133 @@
1
+ // src/utils/index.ts
2
+ import * as readline from 'readline';
3
+ import JSON5 from 'json5';
4
+ import { outputCliError } from '../core/error.js';
5
+ import { parseLooseParams } from './loose-params.js';
6
+ export const LIST_FORMATS = ['json', 'table'];
7
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
8
+ function buildParamsParseError(params) {
9
+ const base = `Invalid JSON in --params: ${params}`;
10
+ if (process.platform !== 'win32') {
11
+ return new Error(base);
12
+ }
13
+ return new Error(`${base}\nHint: Windows PowerShell 5.x may strip quotes inside JSON. Try escaping inner double quotes, switching to pwsh, or using --params-file.`);
14
+ }
15
+ function ensureSafeParams(value) {
16
+ if (!value || typeof value !== 'object') {
17
+ return;
18
+ }
19
+ if (Array.isArray(value)) {
20
+ for (const item of value) {
21
+ ensureSafeParams(item);
22
+ }
23
+ return;
24
+ }
25
+ for (const [key, nested] of Object.entries(value)) {
26
+ if (DANGEROUS_KEYS.has(key)) {
27
+ throw new Error(`Invalid JSON in --params: dangerous key ${key}`);
28
+ }
29
+ ensureSafeParams(nested);
30
+ }
31
+ }
32
+ export function parseCliParams(params) {
33
+ if (!params) {
34
+ return {};
35
+ }
36
+ // PowerShell 可能传来的值被单引号包裹:'{task_id:123}' 或 '{"task_id":123}'
37
+ // 去掉外层单引号
38
+ if (params.startsWith("'") && params.endsWith("'")) {
39
+ params = params.slice(1, -1);
40
+ }
41
+ let parsed;
42
+ try {
43
+ parsed = JSON.parse(params);
44
+ }
45
+ catch {
46
+ try {
47
+ parsed = JSON5.parse(params);
48
+ }
49
+ catch {
50
+ try {
51
+ parsed = parseLooseParams(params);
52
+ }
53
+ catch {
54
+ throw buildParamsParseError(params);
55
+ }
56
+ }
57
+ }
58
+ ensureSafeParams(parsed);
59
+ if (Array.isArray(parsed)) {
60
+ return parsed;
61
+ }
62
+ if (parsed === null || typeof parsed !== 'object') {
63
+ throw new Error('--params must be a JSON object or array');
64
+ }
65
+ return parsed;
66
+ }
67
+ /**
68
+ * 合并简单参数和 --params
69
+ */
70
+ export function mergeParams(options) {
71
+ const { params, user, _, ...flags } = options;
72
+ let jsonParams = {};
73
+ if (params && typeof params === 'string') {
74
+ try {
75
+ const parsed = parseCliParams(params);
76
+ if (Array.isArray(parsed)) {
77
+ throw new Error('--params must be a JSON object');
78
+ }
79
+ jsonParams = parsed;
80
+ }
81
+ catch (error) {
82
+ throw error instanceof Error ? error : new Error('Invalid JSON in --params');
83
+ }
84
+ }
85
+ // 过滤掉 undefined 值
86
+ const cleanFlags = Object.fromEntries(Object.entries(flags).filter(([, v]) => v !== undefined));
87
+ // --params 覆盖同名简单参数
88
+ return { ...cleanFlags, ...jsonParams };
89
+ }
90
+ /**
91
+ * 格式化日期
92
+ */
93
+ export function formatDate(dateStr) {
94
+ const date = new Date(dateStr);
95
+ return date.toLocaleString('zh-CN', {
96
+ year: 'numeric',
97
+ month: '2-digit',
98
+ day: '2-digit',
99
+ hour: '2-digit',
100
+ minute: '2-digit',
101
+ second: '2-digit',
102
+ });
103
+ }
104
+ /**
105
+ * 输出 JSON 结果
106
+ */
107
+ export function outputJson(data) {
108
+ console.log(JSON.stringify(data, null, 2));
109
+ }
110
+ export function parseListFormat(format) {
111
+ if (LIST_FORMATS.includes(format)) {
112
+ return format;
113
+ }
114
+ outputCliError('PARAM_INVALID', `Invalid format: ${format}. Must be one of: ${LIST_FORMATS.join(', ')}`, {
115
+ value: format,
116
+ validFormats: [...LIST_FORMATS],
117
+ });
118
+ }
119
+ /**
120
+ * 确认提示
121
+ */
122
+ export async function confirm(message) {
123
+ const rl = readline.createInterface({
124
+ input: process.stdin,
125
+ output: process.stdout,
126
+ });
127
+ return new Promise((resolve) => {
128
+ rl.question(`${message} (y/N) `, (answer) => {
129
+ rl.close();
130
+ resolve(answer.toLowerCase() === 'y');
131
+ });
132
+ });
133
+ }
@@ -0,0 +1,9 @@
1
+ type TokenType = '{' | '}' | '[' | ']' | ':' | ',' | 'STRING' | 'BARE' | 'NUMBER' | 'BOOLEAN' | 'NULL';
2
+ interface Token {
3
+ type: TokenType;
4
+ value: string;
5
+ pos: number;
6
+ }
7
+ export declare function tokenize(input: string): Token[];
8
+ export declare function parseLooseParams(input: string): Record<string, unknown> | unknown[];
9
+ export type { Token };
@@ -0,0 +1,274 @@
1
+ const DANGEROUS_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
2
+ function isWhitespace(ch) {
3
+ return ch === ' ' || ch === '\t' || ch === '\n' || ch === '\r';
4
+ }
5
+ function isStructuralChar(ch) {
6
+ return ch === '{' || ch === '}' || ch === '[' || ch === ']' || ch === ':' || ch === ',';
7
+ }
8
+ function isValueTerminator(ch) {
9
+ return ch === undefined || isWhitespace(ch) || ch === ',' || ch === '}' || ch === ']';
10
+ }
11
+ function isBareChar(ch) {
12
+ return !isWhitespace(ch)
13
+ && !isStructuralChar(ch)
14
+ && ch !== '"'
15
+ && ch !== '\'';
16
+ }
17
+ export function tokenize(input) {
18
+ const tokens = [];
19
+ let i = 0;
20
+ while (i < input.length) {
21
+ const ch = input[i];
22
+ if (isWhitespace(ch)) {
23
+ i += 1;
24
+ continue;
25
+ }
26
+ if (isStructuralChar(ch)) {
27
+ tokens.push({ type: ch, value: ch, pos: i });
28
+ i += 1;
29
+ continue;
30
+ }
31
+ if (ch === '"' || ch === "'") {
32
+ const quote = ch;
33
+ let j = i + 1;
34
+ let str = '';
35
+ let closed = false;
36
+ while (j < input.length) {
37
+ if (input[j] === '\\' && j + 1 < input.length) {
38
+ str += input[j + 1];
39
+ j += 2;
40
+ continue;
41
+ }
42
+ if (input[j] === quote) {
43
+ closed = true;
44
+ j += 1;
45
+ break;
46
+ }
47
+ str += input[j];
48
+ j += 1;
49
+ }
50
+ if (!closed) {
51
+ throw new Error(`Unterminated string at position ${i}`);
52
+ }
53
+ tokens.push({ type: 'STRING', value: str, pos: i });
54
+ i = j;
55
+ continue;
56
+ }
57
+ if ((ch === '-' && i + 1 < input.length && input[i + 1] >= '0' && input[i + 1] <= '9')
58
+ || (ch >= '0' && ch <= '9')) {
59
+ let j = i;
60
+ if (input[j] === '-') {
61
+ j += 1;
62
+ }
63
+ while (j < input.length && input[j] >= '0' && input[j] <= '9') {
64
+ j += 1;
65
+ }
66
+ if (input[j] === '.' && j + 1 < input.length && input[j + 1] >= '0' && input[j + 1] <= '9') {
67
+ j += 1;
68
+ while (j < input.length && input[j] >= '0' && input[j] <= '9') {
69
+ j += 1;
70
+ }
71
+ }
72
+ if (isValueTerminator(input[j])) {
73
+ tokens.push({ type: 'NUMBER', value: input.slice(i, j), pos: i });
74
+ i = j;
75
+ continue;
76
+ }
77
+ }
78
+ if (isBareChar(ch)) {
79
+ let j = i;
80
+ while (j < input.length && isBareChar(input[j])) {
81
+ j += 1;
82
+ }
83
+ const value = input.slice(i, j);
84
+ if (value === 'true' || value === 'false') {
85
+ tokens.push({ type: 'BOOLEAN', value, pos: i });
86
+ }
87
+ else if (value === 'null') {
88
+ tokens.push({ type: 'NULL', value, pos: i });
89
+ }
90
+ else {
91
+ tokens.push({ type: 'BARE', value, pos: i });
92
+ }
93
+ i = j;
94
+ continue;
95
+ }
96
+ throw new Error(`Unexpected character '${ch}' at position ${i}`);
97
+ }
98
+ return tokens;
99
+ }
100
+ class ParseError extends Error {
101
+ pos;
102
+ constructor(message, pos) {
103
+ super(`${message} at position ${pos}`);
104
+ this.pos = pos;
105
+ this.name = 'ParseError';
106
+ }
107
+ }
108
+ class Parser {
109
+ tokens;
110
+ idx;
111
+ constructor(tokens, idx = 0) {
112
+ this.tokens = tokens;
113
+ this.idx = idx;
114
+ }
115
+ peek() {
116
+ return this.tokens[this.idx];
117
+ }
118
+ advance() {
119
+ const token = this.tokens[this.idx];
120
+ if (!token) {
121
+ throw new ParseError('Unexpected end of input', this.tokens.length);
122
+ }
123
+ this.idx += 1;
124
+ return token;
125
+ }
126
+ expect(type) {
127
+ const token = this.peek();
128
+ if (!token) {
129
+ throw new ParseError(`Expected ${type} but got end of input`, this.tokens.length);
130
+ }
131
+ if (token.type !== type) {
132
+ throw new ParseError(`Expected ${type} but got ${token.type}`, token.pos);
133
+ }
134
+ return this.advance();
135
+ }
136
+ parse() {
137
+ const token = this.peek();
138
+ if (!token) {
139
+ throw new ParseError('Empty input', 0);
140
+ }
141
+ const result = token.type === '{'
142
+ ? this.parseObject()
143
+ : token.type === '['
144
+ ? this.parseArray()
145
+ : (() => { throw new ParseError(`Expected '{' or '[' but got ${token.type}`, token.pos); })();
146
+ const extra = this.peek();
147
+ if (extra) {
148
+ throw new ParseError(`Unexpected trailing token ${extra.type}`, extra.pos);
149
+ }
150
+ return result;
151
+ }
152
+ parseValue() {
153
+ const token = this.peek();
154
+ if (!token) {
155
+ throw new ParseError('Expected value but got end of input', this.tokens.length);
156
+ }
157
+ switch (token.type) {
158
+ case '{':
159
+ return this.parseObject();
160
+ case '[':
161
+ return this.parseArray();
162
+ case 'STRING':
163
+ this.advance();
164
+ return token.value;
165
+ case 'NUMBER':
166
+ return this.parseBareLikeValue();
167
+ case 'BOOLEAN':
168
+ return this.parseBareLikeValue();
169
+ case 'NULL':
170
+ return this.parseBareLikeValue();
171
+ case 'BARE':
172
+ return this.parseBareLikeValue();
173
+ default:
174
+ throw new ParseError(`Unexpected token ${token.type}`, token.pos);
175
+ }
176
+ }
177
+ parseBareLikeValue() {
178
+ const first = this.advance();
179
+ const parts = [first.value];
180
+ while (this.peek()?.type === ':') {
181
+ this.advance();
182
+ const next = this.peek();
183
+ if (!next || (next.type !== 'BARE' && next.type !== 'NUMBER' && next.type !== 'BOOLEAN' && next.type !== 'NULL')) {
184
+ throw new ParseError(`Expected value token after ':'`, this.peek()?.pos ?? this.tokens.length);
185
+ }
186
+ parts.push(':', this.advance().value);
187
+ }
188
+ if (parts.length > 1) {
189
+ return parts.join('');
190
+ }
191
+ switch (first.type) {
192
+ case 'NUMBER':
193
+ return Number(first.value);
194
+ case 'BOOLEAN':
195
+ return first.value === 'true';
196
+ case 'NULL':
197
+ return null;
198
+ case 'BARE':
199
+ return first.value;
200
+ default:
201
+ throw new ParseError(`Unexpected token ${first.type}`, first.pos);
202
+ }
203
+ }
204
+ parseObject() {
205
+ this.expect('{');
206
+ const result = {};
207
+ if (this.peek()?.type === '}') {
208
+ this.advance();
209
+ return result;
210
+ }
211
+ while (true) {
212
+ const keyToken = this.peek();
213
+ if (!keyToken) {
214
+ throw new ParseError('Expected key but got end of input', this.tokens.length);
215
+ }
216
+ if (keyToken.type !== 'BARE' && keyToken.type !== 'STRING') {
217
+ throw new ParseError(`Expected key but got ${keyToken.type}`, keyToken.pos);
218
+ }
219
+ const key = this.advance().value;
220
+ if (DANGEROUS_KEYS.has(key)) {
221
+ throw new ParseError(`Disallowed key ${key}`, keyToken.pos);
222
+ }
223
+ this.expect(':');
224
+ result[key] = this.parseValue();
225
+ const sep = this.peek();
226
+ if (!sep) {
227
+ throw new ParseError(`Expected ',' or '}' but got end of input`, this.tokens.length);
228
+ }
229
+ if (sep.type === ',') {
230
+ this.advance();
231
+ continue;
232
+ }
233
+ if (sep.type === '}') {
234
+ this.advance();
235
+ return result;
236
+ }
237
+ throw new ParseError(`Expected ',' or '}' but got ${sep.type}`, sep.pos);
238
+ }
239
+ }
240
+ parseArray() {
241
+ this.expect('[');
242
+ const result = [];
243
+ if (this.peek()?.type === ']') {
244
+ this.advance();
245
+ return result;
246
+ }
247
+ while (true) {
248
+ result.push(this.parseValue());
249
+ const sep = this.peek();
250
+ if (!sep) {
251
+ throw new ParseError(`Expected ',' or ']' but got end of input`, this.tokens.length);
252
+ }
253
+ if (sep.type === ',') {
254
+ this.advance();
255
+ continue;
256
+ }
257
+ if (sep.type === ']') {
258
+ this.advance();
259
+ return result;
260
+ }
261
+ throw new ParseError(`Expected ',' or ']' but got ${sep.type}`, sep.pos);
262
+ }
263
+ }
264
+ }
265
+ export function parseLooseParams(input) {
266
+ const trimmed = input.trim();
267
+ if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
268
+ throw new Error('Input must start with { or [');
269
+ }
270
+ const result = new Parser(tokenize(trimmed)).parse();
271
+ return Array.isArray(result)
272
+ ? result
273
+ : result;
274
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * 格式化表格输出
3
+ */
4
+ export declare function formatTable(rows: Record<string, unknown>[], columns: string[]): string;
@@ -0,0 +1,25 @@
1
+ // src/utils/table.ts
2
+ /**
3
+ * 格式化表格输出
4
+ */
5
+ export function formatTable(rows, columns) {
6
+ if (rows.length === 0)
7
+ return '';
8
+ // 计算每列最大宽度(表头 + 数据)
9
+ const widths = {};
10
+ for (const col of columns) {
11
+ const dataWidth = Math.max(...rows.map(r => String(r[col] ?? '').length));
12
+ widths[col] = Math.max(col.length, dataWidth);
13
+ }
14
+ // 表头
15
+ const header = columns
16
+ .map(col => col.toUpperCase().padEnd(widths[col]))
17
+ .join(' ');
18
+ // 分隔线
19
+ const separator = columns.map(col => '-'.repeat(widths[col])).join(' ');
20
+ // 数据行
21
+ const body = rows
22
+ .map(row => columns.map(col => String(row[col] ?? '').padEnd(widths[col])).join(' '))
23
+ .join('\n');
24
+ return `${header}\n${separator}\n${body}`;
25
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@meet-im/lxcli",
3
+ "version": "0.0.1",
4
+ "description": "连续科技命令行工具",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "lxcli": "./bin/lxcli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc --build",
12
+ "dev": "tsc --watch",
13
+ "clean": "tsc --build --clean",
14
+ "typecheck": "tsc --noEmit",
15
+ "verify": "pnpm typecheck && pnpm test",
16
+ "prepare": "husky",
17
+ "test": "vitest run",
18
+ "test:watch": "vitest",
19
+ "test:coverage": "vitest run --coverage"
20
+ },
21
+ "engines": {
22
+ "node": ">=22.0.0"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "bin"
27
+ ],
28
+ "keywords": [
29
+ "cli",
30
+ "lxcli"
31
+ ],
32
+ "author": "",
33
+ "license": "ISC",
34
+ "packageManager": "pnpm@10.32.0",
35
+ "dependencies": {
36
+ "commander": "^14.0.3",
37
+ "json5": "^2.2.3",
38
+ "uuid": "^14.0.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.8.0",
42
+ "husky": "^9.1.7",
43
+ "typescript": "^6.0.3",
44
+ "vitest": "^4.1.6"
45
+ }
46
+ }