From db09bc6327c6666b122c54053315e29f6d2b0706 Mon Sep 17 00:00:00 2001 From: owo <17620121969@163.com> Date: Fri, 14 Aug 2026 16:04:38 +0800 Subject: [PATCH] feat: add auth gate and Chinese command parsing Co-authored-by: Cursor --- src/auth.ts | 9 +++++++++ src/commands.ts | 39 +++++++++++++++++++++++++++++++++++++++ tests/auth.test.ts | 16 ++++++++++++++++ tests/commands.test.ts | 18 ++++++++++++++++++ 4 files changed, 82 insertions(+) create mode 100644 src/auth.ts create mode 100644 src/commands.ts create mode 100644 tests/auth.test.ts create mode 100644 tests/commands.test.ts diff --git a/src/auth.ts b/src/auth.ts new file mode 100644 index 0000000..7499c1e --- /dev/null +++ b/src/auth.ts @@ -0,0 +1,9 @@ +export function isAuthorized(opts: { + allowAllUsers: boolean + allowedUserIds: number[] + userId?: number +}): boolean { + if (opts.allowAllUsers) return true + if (opts.userId === undefined) return false + return opts.allowedUserIds.includes(opts.userId) +} diff --git a/src/commands.ts b/src/commands.ts new file mode 100644 index 0000000..407c037 --- /dev/null +++ b/src/commands.ts @@ -0,0 +1,39 @@ +export const MSG = { + DENIED: '无权限。', + WELCOME: + '你好,我是 DeepSeek Harness 渠道机器人。直接发消息开始对话,或发送 /help 查看命令。', + HELP: [ + '/start — 开始 / 欢迎', + '/new 或 /clear — 新开会话', + '/help — 显示帮助', + '', + '仅白名单用户可对话;会话在内存中,重启 Harness 后需重新发消息建立。', + ].join('\n'), + NEW_SESSION: '已开启新会话。', + unknown(command: string): string { + return `未知命令 ${command}。发送 /help 查看可用命令。` + }, +} as const + +export type ParsedCommand = + | { type: 'start'; text: string } + | { type: 'help'; text: string } + | { type: 'new'; text: string } + | { type: 'unknown'; command: string; text: string } + | { type: 'plain'; text: string } + +export function parseCommand(text: string): ParsedCommand { + if (!text.startsWith('/')) return { type: 'plain', text } + const command = text.split(/\s+/)[0] ?? text + switch (command) { + case '/start': + return { type: 'start', text } + case '/help': + return { type: 'help', text } + case '/new': + case '/clear': + return { type: 'new', text } + default: + return { type: 'unknown', command, text } + } +} diff --git a/tests/auth.test.ts b/tests/auth.test.ts new file mode 100644 index 0000000..1b98328 --- /dev/null +++ b/tests/auth.test.ts @@ -0,0 +1,16 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { isAuthorized } from '../src/auth.ts' + +test('empty allowlist rejects even with userId', () => { + assert.equal(isAuthorized({ allowAllUsers: false, allowedUserIds: [], userId: 1 }), false) +}) + +test('allowAllUsers accepts missing userId', () => { + assert.equal(isAuthorized({ allowAllUsers: true, allowedUserIds: [], userId: undefined }), true) +}) + +test('allowlist match', () => { + assert.equal(isAuthorized({ allowAllUsers: false, allowedUserIds: [42], userId: 42 }), true) + assert.equal(isAuthorized({ allowAllUsers: false, allowedUserIds: [42], userId: 7 }), false) +}) diff --git a/tests/commands.test.ts b/tests/commands.test.ts new file mode 100644 index 0000000..c1d862f --- /dev/null +++ b/tests/commands.test.ts @@ -0,0 +1,18 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { MSG, parseCommand } from '../src/commands.ts' + +test('parse slash commands', () => { + assert.equal(parseCommand('/start').type, 'start') + assert.equal(parseCommand('/help extra').type, 'help') + assert.equal(parseCommand('/new').type, 'new') + assert.equal(parseCommand('/clear').type, 'new') + assert.equal(parseCommand('/foo').type, 'unknown') + assert.equal(parseCommand('hello').type, 'plain') +}) + +test('Chinese copy is non-empty', () => { + assert.ok(MSG.DENIED.includes('权限') || MSG.DENIED.includes('授权')) + assert.ok(MSG.HELP.includes('/new')) + assert.ok(MSG.WELCOME.length > 0) +})