@qs_cli/polaris-cli 0.0.0-stage → 0.2.3

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 CHANGED
@@ -1,3 +1,49 @@
1
- # Temporary Holding Version
1
+ # Polaris CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 观星台命令行查询工具,无需打开网页即可查询网络请求和用户账号信息。
4
+
5
+ ## 安装
6
+
7
+ 要求 Node.js 18 或更高版本。默认查询线上服务 `https://polaris.yfcenter.com`。
8
+
9
+ ```bash
10
+ npm install -g @qs_cli/polaris-cli
11
+ ```
12
+
13
+ 也可以在源码目录本地安装:
14
+
15
+ ```bash
16
+ cd polaris_cli
17
+ ./install.sh
18
+ ```
19
+
20
+ 卸载:
21
+
22
+ ```bash
23
+ ./uninstall.sh
24
+ ```
25
+
26
+ ## 快速开始
27
+
28
+ ```bash
29
+ polaris env local # 切换到本地服务
30
+ polaris env online # 切换到线上服务
31
+ polaris login # 浏览器飞书授权
32
+ polaris whoami
33
+ polaris doctor
34
+
35
+ polaris net list --uid 123 --from '2026-11-10 00:00:00'
36
+ polaris net get 100 --curl
37
+ polaris user 123 --env qa
38
+ ```
39
+
40
+ 执行 `polaris help` 或 `polaris <command> --help` 查看完整参数和示例。
41
+
42
+ 配置和不同环境的登录凭证保存在 `~/.polaris/config.json`,文件权限为 `0600`。
43
+
44
+ `polaris` 与 `polaris_cli` 是完全等价的命令入口,例如:
45
+
46
+ ```bash
47
+ polaris net list
48
+ polaris_cli net list
49
+ ```
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env node
2
+ import { Command, CommanderError } from 'commander';
3
+ import { registerAuthCommands } from './commands/auth.js';
4
+ import { registerDoctorCommand } from './commands/doctor.js';
5
+ import { registerEnvCommand } from './commands/env.js';
6
+ import { registerNetCommand } from './commands/net.js';
7
+ import { registerUserCommand } from './commands/user.js';
8
+ import { configureHelp } from './help.js';
9
+ const VERSION = '0.2.3';
10
+ const program = new Command();
11
+ program
12
+ .name('polaris')
13
+ .description('观星台命令行查询工具')
14
+ .version(VERSION)
15
+ .option('--service-env <local|online>', '单次覆盖 Polaris 服务环境')
16
+ .option('--base <url>', '单次覆盖服务 Base URL')
17
+ .option('--json', '以 JSON 输出')
18
+ .helpCommand('help [command]', '显示命令帮助');
19
+ configureHelp(program);
20
+ registerAuthCommands(program);
21
+ registerEnvCommand(program);
22
+ registerDoctorCommand(program, VERSION);
23
+ registerNetCommand(program);
24
+ registerUserCommand(program);
25
+ program.exitOverride();
26
+ async function main() {
27
+ if (process.argv.length <= 2) {
28
+ program.outputHelp();
29
+ return;
30
+ }
31
+ try {
32
+ await program.parseAsync(process.argv);
33
+ }
34
+ catch (error) {
35
+ if (error instanceof CommanderError) {
36
+ if (['commander.helpDisplayed', 'commander.version'].includes(error.code))
37
+ return;
38
+ process.exitCode = error.exitCode || 1;
39
+ return;
40
+ }
41
+ console.error(`错误: ${error instanceof Error ? error.message : String(error)}`);
42
+ process.exitCode = 1;
43
+ }
44
+ }
45
+ await main();
46
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAEzC,MAAM,OAAO,GAAG,OAAO,CAAA;AACvB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,YAAY,CAAC;KACzB,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,8BAA8B,EAAE,mBAAmB,CAAC;KAC3D,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC;KACzC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC;KAC7B,WAAW,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAA;AAE1C,aAAa,CAAC,OAAO,CAAC,CAAA;AACtB,oBAAoB,CAAC,OAAO,CAAC,CAAA;AAC7B,kBAAkB,CAAC,OAAO,CAAC,CAAA;AAC3B,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;AACvC,kBAAkB,CAAC,OAAO,CAAC,CAAA;AAC3B,mBAAmB,CAAC,OAAO,CAAC,CAAA;AAE5B,OAAO,CAAC,YAAY,EAAE,CAAA;AAEtB,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,UAAU,EAAE,CAAA;QACpB,OAAM;IACR,CAAC;IACD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,cAAc,EAAE,CAAC;YACpC,IAAI,CAAC,yBAAyB,EAAE,mBAAmB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,OAAM;YACjF,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAA;YACtC,OAAM;QACR,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QAC9E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED,MAAM,IAAI,EAAE,CAAA"}
@@ -0,0 +1,8 @@
1
+ import type { RequestContext } from './config.js';
2
+ export declare class ApiError extends Error {
3
+ readonly status: number;
4
+ readonly body?: unknown | undefined;
5
+ constructor(message: string, status: number, body?: unknown | undefined);
6
+ }
7
+ export declare function apiRequest<T>(context: RequestContext, path: string, init?: RequestInit, timeoutMs?: number): Promise<T>;
8
+ export declare function queryPath(path: string, params: Record<string, unknown>): string;
package/dist/client.js ADDED
@@ -0,0 +1,63 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ body;
4
+ constructor(message, status, body) {
5
+ super(message);
6
+ this.status = status;
7
+ this.body = body;
8
+ }
9
+ }
10
+ export async function apiRequest(context, path, init = {}, timeoutMs = 15_000) {
11
+ const controller = new AbortController();
12
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
13
+ const headers = new Headers(init.headers);
14
+ headers.set('Accept', 'application/json');
15
+ if (context.token)
16
+ headers.set('Authorization', `Bearer ${context.token}`);
17
+ try {
18
+ const response = await fetch(`${context.baseUrl}${path}`, {
19
+ ...init,
20
+ headers,
21
+ signal: controller.signal,
22
+ });
23
+ const text = await response.text();
24
+ let body;
25
+ try {
26
+ body = text ? JSON.parse(text) : undefined;
27
+ }
28
+ catch {
29
+ body = text;
30
+ }
31
+ if (!response.ok) {
32
+ const serverMessage = body && typeof body === 'object' && 'error' in body
33
+ ? String(body.error)
34
+ : response.statusText;
35
+ const hint = response.status === 401 ? ';请先执行 polaris login' :
36
+ response.status === 403 ? ';当前账号权限不足' : '';
37
+ throw new ApiError(`请求失败 (${response.status}): ${serverMessage}${hint}`, response.status, body);
38
+ }
39
+ return body;
40
+ }
41
+ catch (error) {
42
+ if (error instanceof ApiError)
43
+ throw error;
44
+ if (error instanceof Error && error.name === 'AbortError') {
45
+ throw new Error(`请求超时: ${context.baseUrl}${path}`);
46
+ }
47
+ throw new Error(`无法访问 ${context.baseUrl}: ${error instanceof Error ? error.message : String(error)}`);
48
+ }
49
+ finally {
50
+ clearTimeout(timer);
51
+ }
52
+ }
53
+ export function queryPath(path, params) {
54
+ const search = new URLSearchParams();
55
+ for (const [key, value] of Object.entries(params)) {
56
+ if (value !== undefined && value !== null && value !== '') {
57
+ search.set(key, String(value));
58
+ }
59
+ }
60
+ const query = search.toString();
61
+ return query ? `${path}?${query}` : path;
62
+ }
63
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerAuthCommands(program: Command): void;
@@ -0,0 +1,144 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createServer } from 'node:http';
3
+ import { apiRequest } from '../client.js';
4
+ import { loadConfig, resolveContext, saveConfig, } from '../config.js';
5
+ import { printJson } from '../utils/format.js';
6
+ function tokenExpiresAt(token) {
7
+ if (!token)
8
+ return null;
9
+ try {
10
+ const body = token.split('.')[0];
11
+ const payload = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
12
+ return payload.exp ? new Date(payload.exp * 1000).toISOString() : null;
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ function contextOf(command) {
19
+ return resolveContext(command.optsWithGlobals());
20
+ }
21
+ function openBrowser(url) {
22
+ const [command, args] = process.platform === 'darwin' ? ['open', [url]] :
23
+ process.platform === 'win32' ? ['cmd', ['/c', 'start', '', url]] :
24
+ ['xdg-open', [url]];
25
+ try {
26
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
27
+ child.on('error', () => undefined);
28
+ child.unref();
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ function callbackPage(ok, message) {
36
+ const title = ok ? 'Polaris CLI 授权成功' : 'Polaris CLI 授权失败';
37
+ const safeMessage = message
38
+ .replaceAll('&', '&amp;')
39
+ .replaceAll('<', '&lt;')
40
+ .replaceAll('>', '&gt;')
41
+ .replaceAll('"', '&quot;')
42
+ .replaceAll("'", '&#39;');
43
+ return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>${title}</title></head>
44
+ <body style="font-family:-apple-system,sans-serif;padding:48px"><h2>${title}</h2><p>${safeMessage}</p>
45
+ <script>history.replaceState({}, document.title, location.pathname);</script></body></html>`;
46
+ }
47
+ async function login(command) {
48
+ const context = contextOf(command);
49
+ let server;
50
+ try {
51
+ const listener = await startLoginListener();
52
+ server = listener.server;
53
+ const loginUrl = `${context.baseUrl}/api/v1/auth/feishu/login?cli_port=${listener.port}`;
54
+ console.log(`正在打开浏览器完成 ${context.env} 环境的飞书登录…`);
55
+ console.log(`若浏览器未自动打开,请访问:\n${loginUrl}`);
56
+ openBrowser(loginUrl);
57
+ const token = await listener.token;
58
+ const config = loadConfig();
59
+ config.tokens[context.env] = { token, savedAt: new Date().toISOString() };
60
+ saveConfig(config);
61
+ const authenticated = { ...context, token };
62
+ const me = await apiRequest(authenticated, '/api/v1/auth/me');
63
+ console.log(`登录成功:${me.user?.name || me.user?.email || '未知用户'}(${me.user?.role || '-'})`);
64
+ }
65
+ finally {
66
+ server?.close();
67
+ }
68
+ }
69
+ async function startLoginListener() {
70
+ let resolveToken;
71
+ let rejectToken;
72
+ let settled = false;
73
+ const token = new Promise((resolve, reject) => {
74
+ resolveToken = resolve;
75
+ rejectToken = reject;
76
+ });
77
+ const server = createServer((request, response) => {
78
+ const url = new URL(request.url || '/', 'http://127.0.0.1');
79
+ if (url.pathname !== '/callback') {
80
+ response.writeHead(404).end('Not found');
81
+ return;
82
+ }
83
+ const value = url.searchParams.get('token');
84
+ const callbackError = url.searchParams.get('error');
85
+ if (!value) {
86
+ response.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
87
+ response.end(callbackPage(false, callbackError || '没有收到登录凭证,请返回终端重试。'));
88
+ if (!settled) {
89
+ settled = true;
90
+ rejectToken(new Error(callbackError ? `飞书授权失败: ${callbackError}` : '登录回调缺少 token'));
91
+ }
92
+ return;
93
+ }
94
+ response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
95
+ response.end(callbackPage(true, '凭证已保存,可以关闭此页面并返回终端。'));
96
+ if (!settled) {
97
+ settled = true;
98
+ resolveToken(value);
99
+ }
100
+ });
101
+ await new Promise((resolve, reject) => {
102
+ server.once('error', reject);
103
+ server.listen(0, '127.0.0.1', resolve);
104
+ });
105
+ const timer = setTimeout(() => {
106
+ if (!settled) {
107
+ settled = true;
108
+ rejectToken(new Error('登录等待超时(5 分钟)'));
109
+ }
110
+ }, 5 * 60_000);
111
+ token.finally(() => clearTimeout(timer)).catch(() => undefined);
112
+ return { port: server.address().port, server, token };
113
+ }
114
+ async function whoami(command) {
115
+ const context = contextOf(command);
116
+ const me = await apiRequest(context, '/api/v1/auth/me');
117
+ const expiresAt = tokenExpiresAt(context.token);
118
+ if (context.json) {
119
+ printJson({ ...me, token_expires_at: expiresAt });
120
+ return;
121
+ }
122
+ if (!me.user) {
123
+ console.log('当前服务未启用登录认证。');
124
+ return;
125
+ }
126
+ console.log(`姓名: ${me.user.name || '-'}`);
127
+ console.log(`邮箱: ${me.user.email || '-'}`);
128
+ console.log(`角色: ${me.user.role}`);
129
+ console.log(`过期时间: ${expiresAt ? new Date(expiresAt).toLocaleString('zh-CN', { hour12: false }) : '-'}`);
130
+ console.log(`环境: ${context.env}`);
131
+ }
132
+ function logout(command) {
133
+ const context = contextOf(command);
134
+ const config = loadConfig();
135
+ delete config.tokens[context.env];
136
+ saveConfig(config);
137
+ console.log(`已清除 ${context.env} 的本地登录凭证。`);
138
+ }
139
+ export function registerAuthCommands(program) {
140
+ program.command('login').description('通过浏览器完成飞书登录').action((_options, command) => login(command));
141
+ program.command('logout').description('清除当前环境的登录凭证').action((_options, command) => logout(command));
142
+ program.command('whoami').description('显示当前登录账号与角色').action((_options, command) => whoami(command));
143
+ }
144
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerDoctorCommand(program: Command, version: string): void;
@@ -0,0 +1,111 @@
1
+ import { accessSync, constants } from 'node:fs';
2
+ import { arch, platform, release } from 'node:os';
3
+ import { apiRequest } from '../client.js';
4
+ import { CONFIG_PATH, ENV_URLS, loadConfig, resolveContext, } from '../config.js';
5
+ import { printJson, table } from '../utils/format.js';
6
+ async function probe(baseUrl) {
7
+ const started = performance.now();
8
+ try {
9
+ const response = await fetch(`${baseUrl}/healthz`, {
10
+ signal: AbortSignal.timeout(5_000),
11
+ });
12
+ return {
13
+ ok: response.ok,
14
+ status: response.status,
15
+ elapsedMs: Math.round(performance.now() - started),
16
+ };
17
+ }
18
+ catch (error) {
19
+ return {
20
+ ok: false,
21
+ elapsedMs: Math.round(performance.now() - started),
22
+ error: error instanceof Error ? error.message : String(error),
23
+ };
24
+ }
25
+ }
26
+ export function registerDoctorCommand(program, version) {
27
+ program
28
+ .command('doctor')
29
+ .description('检查 CLI、配置、服务连通性和登录权限')
30
+ .option('--json', '以 JSON 输出')
31
+ .addHelpText('after', `
32
+ 示例:
33
+ polaris doctor
34
+ polaris doctor --json`)
35
+ .action(async (options, command) => {
36
+ const globals = command.optsWithGlobals();
37
+ const context = resolveContext({ ...globals, json: options.json ?? globals.json });
38
+ const config = loadConfig();
39
+ let configReadable = true;
40
+ try {
41
+ accessSync(CONFIG_PATH, constants.R_OK);
42
+ }
43
+ catch {
44
+ configReadable = false;
45
+ }
46
+ const [local, online] = await Promise.all([
47
+ probe(ENV_URLS.local),
48
+ probe(ENV_URLS.online),
49
+ ]);
50
+ let me = null;
51
+ let meError;
52
+ if (context.token) {
53
+ try {
54
+ me = await apiRequest(context, '/api/v1/auth/me', {}, 5_000);
55
+ }
56
+ catch (error) {
57
+ meError = error instanceof Error ? error.message : String(error);
58
+ }
59
+ }
60
+ let userEnvs = null;
61
+ if (context.token) {
62
+ try {
63
+ userEnvs = await apiRequest(context, '/api/v1/user-lookup/envs', {}, 5_000);
64
+ }
65
+ catch {
66
+ // 角色不足时 doctor 的登录检查仍然有效。
67
+ }
68
+ }
69
+ const result = {
70
+ cli: {
71
+ version,
72
+ node: process.version,
73
+ os: `${platform()} ${release()}`,
74
+ arch: arch(),
75
+ executable: process.argv[1],
76
+ },
77
+ config: {
78
+ path: CONFIG_PATH,
79
+ exists: configReadable,
80
+ env: context.env,
81
+ baseUrl: context.baseUrl,
82
+ defaultUserEnv: config.defaultUserEnv,
83
+ },
84
+ connectivity: { local, online },
85
+ auth: {
86
+ hasToken: Boolean(context.token),
87
+ me,
88
+ error: meError,
89
+ },
90
+ userLookupEnvs: userEnvs,
91
+ };
92
+ if (context.json) {
93
+ printJson(result);
94
+ return;
95
+ }
96
+ console.log(table(['检查项', '结果'], [
97
+ ['CLI 版本', version],
98
+ ['Node.js', process.version],
99
+ ['系统', `${platform()} ${arch()}`],
100
+ ['命令路径', process.argv[1]],
101
+ ['配置文件', `${CONFIG_PATH}${configReadable ? '' : '(尚未创建)'}`],
102
+ ['当前环境', `${context.env} (${context.baseUrl})`],
103
+ ['用户查询默认环境', config.defaultUserEnv],
104
+ ['Local 连通性', local.ok ? `正常 HTTP ${local.status} / ${local.elapsedMs}ms` : `失败 / ${local.error || local.status}`],
105
+ ['Online 连通性', online.ok ? `正常 HTTP ${online.status} / ${online.elapsedMs}ms` : `失败 / ${online.error || online.status}`],
106
+ ['登录凭证', context.token ? (meError ? `无效:${meError}` : '有效') : '未登录,请执行 polaris login'],
107
+ ['用户查询环境', userEnvs ? JSON.stringify(userEnvs) : '不可用或权限不足'],
108
+ ], [24, 100]));
109
+ });
110
+ }
111
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerEnvCommand(program: Command): void;
@@ -0,0 +1,38 @@
1
+ import { CONFIG_PATH, ENV_URLS, loadConfig, saveConfig } from '../config.js';
2
+ function useEnv(name) {
3
+ if (name !== 'local' && name !== 'online') {
4
+ throw new Error(`未知环境: ${name};可选值为 local 或 online`);
5
+ }
6
+ const config = loadConfig();
7
+ config.env = name;
8
+ saveConfig(config);
9
+ console.log(`已切换到 ${name}: ${ENV_URLS[name]}`);
10
+ }
11
+ export function registerEnvCommand(program) {
12
+ const env = program
13
+ .command('env [env]')
14
+ .description('查看或切换 Polaris 服务环境')
15
+ .addHelpText('after', `
16
+ 示例:
17
+ polaris env
18
+ polaris env local
19
+ polaris env online
20
+ polaris env use local
21
+ polaris env use online`)
22
+ .action((name) => {
23
+ if (name) {
24
+ useEnv(name);
25
+ return;
26
+ }
27
+ const config = loadConfig();
28
+ console.log(`当前环境: ${config.env}`);
29
+ console.log(`Base URL: ${ENV_URLS[config.env]}`);
30
+ console.log(`配置文件: ${CONFIG_PATH}`);
31
+ console.log('\n切换环境: polaris env local | polaris env online');
32
+ });
33
+ env
34
+ .command('use <env>')
35
+ .description('切换默认服务环境(local 或 online)')
36
+ .action(useEnv);
37
+ }
38
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/commands/env.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAwB,MAAM,cAAc,CAAA;AAElG,SAAS,MAAM,CAAC,IAAY;IAC1B,IAAI,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,sBAAsB,CAAC,CAAA;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,CAAC,GAAG,GAAG,IAAuB,CAAA;IACpC,UAAU,CAAC,MAAM,CAAC,CAAA;IAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;AAChD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAgB;IACjD,MAAM,GAAG,GAAG,OAAO;SAChB,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,oBAAoB,CAAC;SACjC,WAAW,CAAC,OAAO,EAAE;;;;;;yBAMD,CAAC;SACrB,MAAM,CAAC,CAAC,IAAa,EAAE,EAAE;QACxB,IAAI,IAAI,EAAE,CAAC;YACT,MAAM,CAAC,IAAI,CAAC,CAAA;YACZ,OAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;QAC3B,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,GAAG,EAAE,CAAC,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACnD,OAAO,CAAC,GAAG,CAAC,YAAY,WAAW,EAAE,CAAC,CAAA;QACtC,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAA;IAC/D,CAAC,CAAC,CAAA;IAEJ,GAAG;SACA,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,0BAA0B,CAAC;SACvC,MAAM,CAAC,MAAM,CAAC,CAAA;AACnB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerNetCommand(program: Command): void;
@@ -0,0 +1,152 @@
1
+ import { apiRequest, queryPath } from '../client.js';
2
+ import { resolveContext } from '../config.js';
3
+ import { generateCurl } from '../utils/curl.js';
4
+ import { formatDate, limitLines, prettyContent, printJson, table, } from '../utils/format.js';
5
+ function positiveInt(value, name, max) {
6
+ const parsed = Number(value);
7
+ if (!Number.isInteger(parsed) || parsed <= 0 || (max !== undefined && parsed > max)) {
8
+ throw new Error(`${name} 必须是 1${max ? `-${max}` : ' 以上'}的整数`);
9
+ }
10
+ return parsed;
11
+ }
12
+ async function listNet(options, command) {
13
+ const context = resolveContext(command.optsWithGlobals());
14
+ const page = positiveInt(options.page, '--page');
15
+ const size = positiveInt(options.size, '--size', 200);
16
+ if (options.success && !['true', 'false'].includes(options.success)) {
17
+ throw new Error('--success 只能是 true 或 false');
18
+ }
19
+ if (options.bizCode && options.bizCode !== 'nonzero' && !/^-?\d+$/.test(options.bizCode)) {
20
+ throw new Error('--biz-code 只能是整数或 nonzero');
21
+ }
22
+ const path = queryPath('/api/v1/net', {
23
+ id: options.id,
24
+ user_uid: options.uid,
25
+ device_id: options.device,
26
+ method: options.method?.toUpperCase(),
27
+ host: options.host,
28
+ keyword: options.keyword,
29
+ status_code: options.status,
30
+ success: options.success,
31
+ biz_code: options.bizCode,
32
+ min_duration: options.minDuration,
33
+ app_id: options.appId,
34
+ app_version: options.appVersion,
35
+ from: options.from,
36
+ to: options.to,
37
+ page,
38
+ size,
39
+ });
40
+ const result = await apiRequest(context, path);
41
+ if (context.json) {
42
+ printJson(result);
43
+ return;
44
+ }
45
+ console.log(`共 ${result.total} 条,第 ${result.page} 页,每页 ${result.size} 条`);
46
+ if (!result.items.length) {
47
+ console.log('没有匹配的网络请求。');
48
+ return;
49
+ }
50
+ console.log(table(['ID', '时间', '方法', '状态', '耗时', 'UID', 'Host', 'Path'], result.items.map((item) => [
51
+ item.id,
52
+ formatDate(item.client_time),
53
+ item.method,
54
+ item.status_code,
55
+ `${item.duration_ms ?? 0}ms`,
56
+ item.user_uid,
57
+ item.host,
58
+ item.path,
59
+ ]), [10, 20, 8, 8, 10, 16, 28, 48]));
60
+ }
61
+ function section(title, content) {
62
+ console.log(`\n${title}\n${'─'.repeat(Math.max(8, title.length * 2))}`);
63
+ console.log(content);
64
+ }
65
+ async function getNet(idRaw, options, command) {
66
+ const id = positiveInt(idRaw, 'id');
67
+ const maxLines = positiveInt(options.maxLines, '--max-lines', 100_000);
68
+ const context = resolveContext(command.optsWithGlobals());
69
+ const item = await apiRequest(context, `/api/v1/net/${id}`);
70
+ if (context.json) {
71
+ printJson(item);
72
+ return;
73
+ }
74
+ section('概要', table(['方法', 'URL', '状态码', '耗时', '成功', '业务码'], [[item.method, item.url, item.status_code, `${item.duration_ms}ms`, item.success, item.biz_code]], [8, 80, 8, 10, 8, 10]));
75
+ if (item.error)
76
+ console.log(`错误: ${item.error}`);
77
+ section('基本信息', table(['字段', '值'], [
78
+ ['Host', item.host],
79
+ ['Path', item.path],
80
+ ['平台', item.platform],
81
+ ['App ID', item.app_id],
82
+ ['App 版本', item.app_version],
83
+ ['OS 版本', item.os_version],
84
+ ['UID', item.user_uid],
85
+ ['设备 ID', item.device_id],
86
+ ['IP', item.ip],
87
+ ['客户端时间', formatDate(item.client_time)],
88
+ ['服务端时间', formatDate(item.server_time)],
89
+ ], [16, 100]));
90
+ try {
91
+ const url = new URL(String(item.url));
92
+ const params = [...url.searchParams.entries()];
93
+ section('Query 参数', params.length
94
+ ? table(['参数名', '参数值'], params, [30, 100])
95
+ : '(无 query 参数)');
96
+ }
97
+ catch {
98
+ section('Query 参数', '(URL 无法解析)');
99
+ }
100
+ section('请求头', prettyContent(item.request_headers));
101
+ section('请求体', prettyContent(item.request_body));
102
+ if (options.respHeaders || options.all) {
103
+ section('响应头', prettyContent(item.response_headers));
104
+ }
105
+ section('响应体', limitLines(prettyContent(item.response_body), maxLines));
106
+ if (options.curl || options.all) {
107
+ section('curl', generateCurl(item));
108
+ }
109
+ }
110
+ export function registerNetCommand(program) {
111
+ const net = program.command('net').description('查询网络请求');
112
+ net
113
+ .command('list')
114
+ .description('按多种条件查询网络请求列表')
115
+ .option('--id <number>', '按记录 ID 精确查询')
116
+ .option('--uid <string>', '按用户 UID 查询')
117
+ .option('--device <string>', '按设备 ID 查询')
118
+ .option('--method <method>', 'HTTP 方法')
119
+ .option('--host <string>', 'Host')
120
+ .option('--keyword <string>', 'URL/Path 关键词')
121
+ .option('--status <number>', 'HTTP 状态码')
122
+ .option('--success <true|false>', '是否成功')
123
+ .option('--biz-code <number|nonzero>', '业务码')
124
+ .option('--min-duration <ms>', '最小耗时(毫秒)')
125
+ .option('--app-id <string>', 'App ID')
126
+ .option('--app-version <string>', 'App 版本')
127
+ .option('--from <time>', '开始时间')
128
+ .option('--to <time>', '结束时间')
129
+ .option('--page <number>', '页码', '1')
130
+ .option('--size <number>', '每页条数,最大 200', '20')
131
+ .addHelpText('after', `
132
+ 示例:
133
+ polaris net list --uid 123
134
+ polaris net list --method POST --status 500 --from "2026-11-10 00:00:00"
135
+ polaris net list --biz-code nonzero --min-duration 1000 --json`)
136
+ .action(listNet);
137
+ net
138
+ .command('get <id>')
139
+ .description('查看一条网络请求详情')
140
+ .option('--curl', '显示可执行 curl 命令')
141
+ .option('--resp-headers', '显示响应头')
142
+ .option('--all', '显示响应头和 curl')
143
+ .option('--max-lines <number>', '响应体最大行数', '80')
144
+ .addHelpText('after', `
145
+ 示例:
146
+ polaris net get 100
147
+ polaris net get 100 --curl
148
+ polaris net get 100 --all --max-lines 200
149
+ polaris net get 100 --json`)
150
+ .action(getNet);
151
+ }
152
+ //# sourceMappingURL=net.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"net.js","sourceRoot":"","sources":["../../src/commands/net.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerUserCommand(program: Command): void;
@@ -0,0 +1,93 @@
1
+ import { apiRequest, queryPath } from '../client.js';
2
+ import { loadConfig, resolveContext } from '../config.js';
3
+ import { maskEmail, maskMobile, printJson, table } from '../utils/format.js';
4
+ function uidTypeName(value) {
5
+ if (value === 0)
6
+ return '普通';
7
+ if (value === 1)
8
+ return '特殊';
9
+ if (value === 2)
10
+ return '预留';
11
+ return String(value);
12
+ }
13
+ async function lookupUser(positionalUID, options, command) {
14
+ const context = resolveContext(command.optsWithGlobals());
15
+ const uid = options.uid || positionalUID;
16
+ const queryCount = [uid, options.email, options.mobile].filter(Boolean).length;
17
+ if (queryCount !== 1) {
18
+ throw new Error('请且仅请提供一种查询条件:UID、--email 或 --mobile');
19
+ }
20
+ if (uid && !/^[1-9]\d*$/.test(uid)) {
21
+ throw new Error('UID 必须是正整数');
22
+ }
23
+ if (options.nationCode && !options.mobile) {
24
+ throw new Error('--nation-code 只能与 --mobile 一起使用');
25
+ }
26
+ const env = options.env || loadConfig().defaultUserEnv;
27
+ if (!['dev', 'qa'].includes(env)) {
28
+ throw new Error('--env 只能是 dev 或 qa');
29
+ }
30
+ const path = queryPath('/api/v1/user-lookup', {
31
+ env,
32
+ uid,
33
+ email: options.email,
34
+ mobile: options.mobile,
35
+ nation_code: options.nationCode,
36
+ });
37
+ const result = await apiRequest(context, path);
38
+ const account = {
39
+ ...result.account,
40
+ email: options.reveal ? result.account.email : maskEmail(result.account.email || ''),
41
+ mobile: options.reveal ? result.account.mobile : maskMobile(result.account.mobile || ''),
42
+ };
43
+ const output = { ...result, account };
44
+ if (context.json) {
45
+ printJson(output);
46
+ return;
47
+ }
48
+ console.log(table(['字段', '值'], [
49
+ ['环境', result.env.toUpperCase()],
50
+ ['UID', account.uid],
51
+ ['账号类型', uidTypeName(account.uid_type)],
52
+ ['昵称', account.nickname],
53
+ ['区号', account.nation_code],
54
+ ['手机号', account.mobile],
55
+ ['邮箱', account.email],
56
+ ['语言', account.language],
57
+ ['头像', account.avatar],
58
+ ], [16, 100]));
59
+ if (!options.reveal)
60
+ console.log('\n手机号和邮箱已脱敏;需要明文时添加 --reveal。');
61
+ }
62
+ export function registerUserCommand(program) {
63
+ const user = program
64
+ .command('user [uid]')
65
+ .description('在 Dev/QA 用户中心查询账号')
66
+ .option('--uid <uid>', '按 UID 查询(也可使用位置参数)')
67
+ .option('--env <dev|qa>', '用户中心环境(默认 qa)')
68
+ .option('--mobile <mobile>', '按手机号反查')
69
+ .option('--nation-code <code>', '手机号国家/地区码')
70
+ .option('--email <email>', '按邮箱反查')
71
+ .option('--reveal', '显示手机号和邮箱明文')
72
+ .addHelpText('after', `
73
+ 示例:
74
+ polaris user 123 --env qa
75
+ polaris user --uid 123 --env dev
76
+ polaris user --mobile 91234567 --nation-code 852
77
+ polaris user --email user@example.com --reveal
78
+ polaris user envs`)
79
+ .action(lookupUser);
80
+ user
81
+ .command('envs')
82
+ .description('查看后端已配置的用户查询环境')
83
+ .action(async (_options, command) => {
84
+ const context = resolveContext(command.optsWithGlobals());
85
+ const result = await apiRequest(context, '/api/v1/user-lookup/envs');
86
+ if (context.json) {
87
+ printJson(result);
88
+ return;
89
+ }
90
+ console.log(table(['环境', '名称'], result.items.map((item) => [item.key, item.label]), [12, 40]));
91
+ });
92
+ }
93
+ //# sourceMappingURL=user.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.js","sourceRoot":"","sources":["../../src/commands/user.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export type EnvironmentName = 'local' | 'online';
2
+ export interface StoredToken {
3
+ token: string;
4
+ savedAt: string;
5
+ }
6
+ export interface PolarisConfig {
7
+ env: EnvironmentName;
8
+ defaultUserEnv: 'dev' | 'qa';
9
+ tokens: Partial<Record<EnvironmentName, StoredToken>>;
10
+ }
11
+ export interface GlobalOptions {
12
+ serviceEnv?: EnvironmentName;
13
+ base?: string;
14
+ json?: boolean;
15
+ }
16
+ export interface RequestContext {
17
+ env: EnvironmentName;
18
+ baseUrl: string;
19
+ token?: string;
20
+ json: boolean;
21
+ }
22
+ export declare const ENV_URLS: Record<EnvironmentName, string>;
23
+ export declare const CONFIG_PATH: string;
24
+ export declare function loadConfig(): PolarisConfig;
25
+ export declare function saveConfig(config: PolarisConfig): void;
26
+ export declare function normalizeBaseUrl(value: string): string;
27
+ export declare function resolveContext(options: GlobalOptions): RequestContext;
package/dist/config.js ADDED
@@ -0,0 +1,55 @@
1
+ import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ export const ENV_URLS = {
5
+ local: 'http://127.0.0.1:8080',
6
+ online: 'https://polaris.yfcenter.com',
7
+ };
8
+ export const CONFIG_PATH = process.env.POLARIS_CONFIG || join(homedir(), '.polaris', 'config.json');
9
+ const DEFAULT_CONFIG = {
10
+ env: 'online',
11
+ defaultUserEnv: 'qa',
12
+ tokens: {},
13
+ };
14
+ export function loadConfig() {
15
+ try {
16
+ const parsed = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));
17
+ return {
18
+ env: parsed.env === 'local' || parsed.env === 'online' ? parsed.env : DEFAULT_CONFIG.env,
19
+ defaultUserEnv: parsed.defaultUserEnv === 'dev' ? 'dev' : 'qa',
20
+ tokens: parsed.tokens && typeof parsed.tokens === 'object' ? parsed.tokens : {},
21
+ };
22
+ }
23
+ catch {
24
+ return structuredClone(DEFAULT_CONFIG);
25
+ }
26
+ }
27
+ export function saveConfig(config) {
28
+ const dir = dirname(CONFIG_PATH);
29
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
30
+ const temp = `${CONFIG_PATH}.${process.pid}.tmp`;
31
+ writeFileSync(temp, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
32
+ renameSync(temp, CONFIG_PATH);
33
+ chmodSync(CONFIG_PATH, 0o600);
34
+ }
35
+ export function normalizeBaseUrl(value) {
36
+ const url = new URL(value);
37
+ if (!['http:', 'https:'].includes(url.protocol)) {
38
+ throw new Error('--base 只支持 http 或 https URL');
39
+ }
40
+ return url.toString().replace(/\/+$/, '');
41
+ }
42
+ export function resolveContext(options) {
43
+ const config = loadConfig();
44
+ const env = options.serviceEnv ?? config.env;
45
+ if (env !== 'local' && env !== 'online') {
46
+ throw new Error('--service-env 只能是 local 或 online');
47
+ }
48
+ return {
49
+ env,
50
+ baseUrl: options.base ? normalizeBaseUrl(options.base) : ENV_URLS[env],
51
+ token: config.tokens[env]?.token,
52
+ json: Boolean(options.json),
53
+ };
54
+ }
55
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/help.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import type { Command } from 'commander';
2
+ export declare function configureHelp(program: Command): void;
package/dist/help.js ADDED
@@ -0,0 +1,23 @@
1
+ import { CONFIG_PATH, ENV_URLS, loadConfig } from './config.js';
2
+ export function configureHelp(program) {
3
+ const config = loadConfig();
4
+ const loggedIn = Boolean(config.tokens[config.env]?.token);
5
+ program
6
+ .showHelpAfterError('(执行 polaris help 查看完整帮助)')
7
+ .showSuggestionAfterError()
8
+ .addHelpText('beforeAll', 'Polaris CLI - 观星台命令行查询工具\n')
9
+ .addHelpText('afterAll', `
10
+ 当前状态:
11
+ 环境: ${config.env} (${ENV_URLS[config.env]})
12
+ 登录: ${loggedIn ? '已保存凭证' : '未登录'}
13
+ 配置: ${CONFIG_PATH}
14
+
15
+ 常用示例:
16
+ polaris env online
17
+ polaris login
18
+ polaris net list --uid 123 --status 500
19
+ polaris net get 100 --curl
20
+ polaris user 123 --env qa
21
+ polaris doctor`);
22
+ }
23
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE/D,MAAM,UAAU,aAAa,CAAC,OAAgB;IAC5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAA;IAC1D,OAAO;SACJ,kBAAkB,CAAC,0BAA0B,CAAC;SAC9C,wBAAwB,EAAE;SAC1B,WAAW,CAAC,WAAW,EAAE,4BAA4B,CAAC;SACtD,WAAW,CAAC,UAAU,EAAE;;UAEnB,MAAM,CAAC,GAAG,KAAK,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC;UACnC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK;UAC1B,WAAW;;;;;;;;iBAQJ,CAAC,CAAA;AAClB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function generateCurl(request: Record<string, unknown>): string;
@@ -0,0 +1,43 @@
1
+ const MANAGED_HEADERS = new Set([
2
+ 'content-length',
3
+ 'host',
4
+ 'connection',
5
+ 'transfer-encoding',
6
+ 'accept-encoding',
7
+ ]);
8
+ function shellQuote(value) {
9
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
10
+ }
11
+ function parseHeaders(value) {
12
+ if (!value)
13
+ return {};
14
+ if (typeof value === 'object')
15
+ return value;
16
+ try {
17
+ const parsed = JSON.parse(String(value));
18
+ return parsed && typeof parsed === 'object' ? parsed : {};
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ export function generateCurl(request) {
25
+ const method = String(request.method || 'GET').toUpperCase();
26
+ const lines = [`curl --request ${method} ${shellQuote(request.url || '')}`];
27
+ for (const [key, rawValue] of Object.entries(parseHeaders(request.request_headers))) {
28
+ if (MANAGED_HEADERS.has(key.toLowerCase()))
29
+ continue;
30
+ const value = Array.isArray(rawValue)
31
+ ? rawValue.join(', ')
32
+ : typeof rawValue === 'object' && rawValue !== null
33
+ ? JSON.stringify(rawValue)
34
+ : String(rawValue ?? '');
35
+ lines.push(` --header ${shellQuote(`${key}: ${value}`)}`);
36
+ }
37
+ const body = request.request_body;
38
+ if (body !== null && body !== undefined && body !== '') {
39
+ lines.push(` --data-raw ${shellQuote(typeof body === 'string' ? body : JSON.stringify(body))}`);
40
+ }
41
+ return lines.join(' \\\n');
42
+ }
43
+ //# sourceMappingURL=curl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"curl.js","sourceRoot":"","sources":["../../src/utils/curl.ts"],"names":[],"mappings":"AAAA,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,gBAAgB;IAChB,MAAM;IACN,YAAY;IACZ,mBAAmB;IACnB,iBAAiB;CAClB,CAAC,CAAA;AAEF,SAAS,UAAU,CAAC,KAAc;IAChC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAA;AACpD,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAA;IACrB,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAgC,CAAA;IACtE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;QACxC,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAiC,CAAC,CAAC,CAAC,EAAE,CAAA;IACtF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgC;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;IAC5D,MAAM,KAAK,GAAG,CAAC,kBAAkB,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3E,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;QACpF,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YAAE,SAAQ;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YACnC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YACrB,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,IAAI;gBACjD,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;gBAC1B,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAA;QAC5B,KAAK,CAAC,IAAI,CAAC,cAAc,UAAU,CAAC,GAAG,GAAG,KAAK,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IAC5D,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY,CAAA;IACjC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QACvD,KAAK,CAAC,IAAI,CAAC,gBAAgB,UAAU,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAA;IAClG,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;AAC5B,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type TableValue = string | number | boolean | null | undefined;
2
+ export declare function table(headers: string[], rows: TableValue[][], maxWidths?: number[]): string;
3
+ export declare function printJson(value: unknown): void;
4
+ export declare function formatDate(value: unknown): string;
5
+ export declare function prettyContent(value: unknown): string;
6
+ export declare function limitLines(value: string, maxLines: number): string;
7
+ export declare function maskEmail(value: string): string;
8
+ export declare function maskMobile(value: string): string;
@@ -0,0 +1,70 @@
1
+ function text(value) {
2
+ if (value === null || value === undefined || value === '')
3
+ return '-';
4
+ return String(value).replace(/\r?\n/g, ' ');
5
+ }
6
+ function clipped(value, width) {
7
+ if (value.length <= width)
8
+ return value;
9
+ return `${value.slice(0, Math.max(1, width - 1))}…`;
10
+ }
11
+ export function table(headers, rows, maxWidths = []) {
12
+ const normalized = rows.map((row) => row.map(text));
13
+ const widths = headers.map((header, index) => {
14
+ const contentWidth = Math.max(header.length, ...normalized.map((row) => row[index]?.length ?? 0));
15
+ return Math.min(contentWidth, maxWidths[index] ?? 40);
16
+ });
17
+ const line = (row) => row.map((value, index) => clipped(value ?? '', widths[index]).padEnd(widths[index])).join(' ').trimEnd();
18
+ const separator = widths.map((width) => '─'.repeat(width)).join(' ');
19
+ return [line(headers), separator, ...normalized.map(line)].join('\n');
20
+ }
21
+ export function printJson(value) {
22
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
23
+ }
24
+ export function formatDate(value) {
25
+ if (value === null || value === undefined || value === '')
26
+ return '-';
27
+ const date = typeof value === 'number' ? new Date(value) : new Date(String(value));
28
+ return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString('zh-CN', { hour12: false });
29
+ }
30
+ export function prettyContent(value) {
31
+ if (value === null || value === undefined || value === '')
32
+ return '(空)';
33
+ if (typeof value !== 'string')
34
+ return JSON.stringify(value, null, 2);
35
+ try {
36
+ return JSON.stringify(JSON.parse(value), null, 2);
37
+ }
38
+ catch {
39
+ return value;
40
+ }
41
+ }
42
+ export function limitLines(value, maxLines) {
43
+ const lines = value.split(/\r?\n/);
44
+ if (lines.length <= maxLines)
45
+ return value;
46
+ return `${lines.slice(0, maxLines).join('\n')}\n…(已省略 ${lines.length - maxLines} 行,使用 --max-lines 调整)`;
47
+ }
48
+ export function maskEmail(value) {
49
+ if (!value)
50
+ return '';
51
+ const [name, domain] = value.split('@');
52
+ if (!domain)
53
+ return maskText(value);
54
+ const visible = name.length <= 2 ? name.slice(0, 1) : name.slice(0, 2);
55
+ return `${visible}${'*'.repeat(Math.max(2, name.length - visible.length))}@${domain}`;
56
+ }
57
+ export function maskMobile(value) {
58
+ const plain = value.trim();
59
+ if (!plain)
60
+ return '';
61
+ if (plain.length <= 5)
62
+ return maskText(plain);
63
+ return `${plain.slice(0, 3)}${'*'.repeat(plain.length - 5)}${plain.slice(-2)}`;
64
+ }
65
+ function maskText(value) {
66
+ if (value.length <= 1)
67
+ return '*';
68
+ return `${value[0]}${'*'.repeat(Math.max(2, value.length - 1))}`;
69
+ }
70
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "name": "@qs_cli/polaris-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.2.3",
4
+ "description": "观星台命令行查询工具",
5
+ "type": "module",
6
+ "bin": {
7
+ "polaris": "dist/cli.js",
8
+ "polaris_cli": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/cli.js',0o755)\"",
16
+ "start": "node dist/cli.js",
17
+ "check": "tsc -p tsconfig.json --noEmit"
18
+ },
19
+ "publishConfig": {
20
+ "registry": "https://registry.npmjs.org/",
21
+ "access": "public"
22
+ },
23
+ "engines": {
24
+ "node": ">=18"
25
+ },
26
+ "dependencies": {
27
+ "commander": "^13.1.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.15.3",
31
+ "typescript": "^5.8.3"
32
+ }
33
+ }