mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-05 00:47:48 +08:00
feat: expose remote control as admin module
This commit is contained in:
@@ -92,7 +92,7 @@ npm --prefix aether-vscodex/web run build
|
|||||||
`vscodex_data`,短期配对码、60 秒一次性浏览器票据和在线房间保存在内存。不要在未引入
|
`vscodex_data`,短期配对码、60 秒一次性浏览器票据和在线房间保存在内存。不要在未引入
|
||||||
共享连接目录前横向扩容 sidecar。
|
共享连接目录前横向扩容 sidecar。
|
||||||
|
|
||||||
登录 Aether 后打开“Codex 远程控制”,生成一次性配对码。然后在 VS Code 命令面板执行
|
登录 Aether 后打开“远程控制”,生成一次性配对码。然后在 VS Code 命令面板执行
|
||||||
**Codex Remote: Pair with Aether**,填写 Aether 地址和配对码。插件会把设备凭据写入
|
**Codex Remote: Pair with Aether**,填写 Aether 地址和配对码。插件会把设备凭据写入
|
||||||
VS Code SecretStorage,并同时保持本机控制台连接。
|
VS Code SecretStorage,并同时保持本机控制台连接。
|
||||||
|
|
||||||
|
|||||||
@@ -122,6 +122,18 @@ pub(crate) const ADMIN_MODULE_DEFINITIONS: &[AdminModuleDefinition] = &[
|
|||||||
admin_menu_group: None,
|
admin_menu_group: None,
|
||||||
admin_menu_order: 59,
|
admin_menu_order: 59,
|
||||||
},
|
},
|
||||||
|
AdminModuleDefinition {
|
||||||
|
name: "vscodex",
|
||||||
|
display_name: "远程控制",
|
||||||
|
description: "通过 aether-vscodex 插件远程控制本地 Codex 工作区",
|
||||||
|
category: "integration",
|
||||||
|
env_key: "VSCODEX_AVAILABLE",
|
||||||
|
default_available: true,
|
||||||
|
admin_route: Some("/dashboard/vscodex"),
|
||||||
|
admin_menu_icon: Some("SquareTerminal"),
|
||||||
|
admin_menu_group: Some("overview"),
|
||||||
|
admin_menu_order: 80,
|
||||||
|
},
|
||||||
AdminModuleDefinition {
|
AdminModuleDefinition {
|
||||||
name: "s3_backup",
|
name: "s3_backup",
|
||||||
display_name: "S3 备份",
|
display_name: "S3 备份",
|
||||||
|
|||||||
@@ -275,8 +275,8 @@ export const messages = {
|
|||||||
'nav.healthMonitor': '健康监控',
|
'nav.healthMonitor': '健康监控',
|
||||||
'nav.modelCatalog': '模型目录',
|
'nav.modelCatalog': '模型目录',
|
||||||
'nav.apiKeys': 'API 密钥',
|
'nav.apiKeys': 'API 密钥',
|
||||||
'nav.vscodex': 'Codex 远程控制',
|
'nav.vscodex': '远程控制',
|
||||||
'vscodex.title': 'Codex 远程控制',
|
'vscodex.title': '远程控制',
|
||||||
'vscodex.devices.label': '控制设备',
|
'vscodex.devices.label': '控制设备',
|
||||||
'vscodex.devices.refresh': '刷新设备',
|
'vscodex.devices.refresh': '刷新设备',
|
||||||
'vscodex.devices.loading': '正在加载设备...',
|
'vscodex.devices.loading': '正在加载设备...',
|
||||||
@@ -305,7 +305,7 @@ export const messages = {
|
|||||||
'vscodex.connection.connecting': '正在建立安全连接...',
|
'vscodex.connection.connecting': '正在建立安全连接...',
|
||||||
'vscodex.connection.ticketFailed': '安全连接凭证获取失败。',
|
'vscodex.connection.ticketFailed': '安全连接凭证获取失败。',
|
||||||
'vscodex.connection.retry': '重试',
|
'vscodex.connection.retry': '重试',
|
||||||
'vscodex.frame.title': 'Codex 远程控制台',
|
'vscodex.frame.title': '远程控制台',
|
||||||
'nav.walletCenter': '钱包中心',
|
'nav.walletCenter': '钱包中心',
|
||||||
'nav.billingCenter': '套餐中心',
|
'nav.billingCenter': '套餐中心',
|
||||||
'nav.myReferral': '我的邀请',
|
'nav.myReferral': '我的邀请',
|
||||||
@@ -692,8 +692,8 @@ export const messages = {
|
|||||||
'nav.healthMonitor': 'Health monitor',
|
'nav.healthMonitor': 'Health monitor',
|
||||||
'nav.modelCatalog': 'Model catalog',
|
'nav.modelCatalog': 'Model catalog',
|
||||||
'nav.apiKeys': 'API keys',
|
'nav.apiKeys': 'API keys',
|
||||||
'nav.vscodex': 'Codex remote control',
|
'nav.vscodex': 'Remote control',
|
||||||
'vscodex.title': 'Codex remote control',
|
'vscodex.title': 'Remote control',
|
||||||
'vscodex.devices.label': 'Control device',
|
'vscodex.devices.label': 'Control device',
|
||||||
'vscodex.devices.refresh': 'Refresh devices',
|
'vscodex.devices.refresh': 'Refresh devices',
|
||||||
'vscodex.devices.loading': 'Loading devices...',
|
'vscodex.devices.loading': 'Loading devices...',
|
||||||
@@ -722,7 +722,7 @@ export const messages = {
|
|||||||
'vscodex.connection.connecting': 'Establishing a secure connection...',
|
'vscodex.connection.connecting': 'Establishing a secure connection...',
|
||||||
'vscodex.connection.ticketFailed': 'A secure connection ticket could not be issued.',
|
'vscodex.connection.ticketFailed': 'A secure connection ticket could not be issued.',
|
||||||
'vscodex.connection.retry': 'Retry',
|
'vscodex.connection.retry': 'Retry',
|
||||||
'vscodex.frame.title': 'Codex remote control surface',
|
'vscodex.frame.title': 'Remote control surface',
|
||||||
'nav.walletCenter': 'Wallet',
|
'nav.walletCenter': 'Wallet',
|
||||||
'nav.billingCenter': 'Plans',
|
'nav.billingCenter': 'Plans',
|
||||||
'nav.myReferral': 'Referrals',
|
'nav.myReferral': 'Referrals',
|
||||||
|
|||||||
@@ -37,19 +37,27 @@ describe('main layout navigation builder', () => {
|
|||||||
expect(navigation.flatMap(group => group.items.map(item => item.name))).toContain('tx:nav.myReferral')
|
expect(navigation.flatMap(group => group.items.map(item => item.name))).toContain('tx:nav.myReferral')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('exposes the same VS Code control destination to users and administrators', () => {
|
it('exposes remote control to users and active administrators', () => {
|
||||||
const commonOptions = {
|
const userNavigation = buildNavigation({
|
||||||
|
canAccessAdmin: false,
|
||||||
modules: {},
|
modules: {},
|
||||||
isModuleActive: () => false,
|
isModuleActive: () => false,
|
||||||
t: translate,
|
t: translate,
|
||||||
}
|
|
||||||
const userNavigation = buildNavigation({
|
|
||||||
...commonOptions,
|
|
||||||
canAccessAdmin: false,
|
|
||||||
})
|
})
|
||||||
const adminNavigation = buildNavigation({
|
const adminNavigation = buildNavigation({
|
||||||
...commonOptions,
|
|
||||||
canAccessAdmin: true,
|
canAccessAdmin: true,
|
||||||
|
modules: {
|
||||||
|
vscodex: {
|
||||||
|
active: true,
|
||||||
|
admin_route: '/dashboard/vscodex',
|
||||||
|
admin_menu_group: 'overview',
|
||||||
|
admin_menu_order: 80,
|
||||||
|
admin_menu_icon: 'SquareTerminal',
|
||||||
|
display_name: '远程控制',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isModuleActive: () => false,
|
||||||
|
t: translate,
|
||||||
})
|
})
|
||||||
|
|
||||||
const findVscodeControl = (navigation: ReturnType<typeof buildNavigation>) => (
|
const findVscodeControl = (navigation: ReturnType<typeof buildNavigation>) => (
|
||||||
@@ -63,9 +71,14 @@ describe('main layout navigation builder', () => {
|
|||||||
href: '/dashboard/vscodex',
|
href: '/dashboard/vscodex',
|
||||||
})
|
})
|
||||||
expect(findVscodeControl(adminNavigation)).toMatchObject({
|
expect(findVscodeControl(adminNavigation)).toMatchObject({
|
||||||
name: 'tx:nav.vscodex',
|
name: '远程控制',
|
||||||
href: '/dashboard/vscodex',
|
href: '/dashboard/vscodex',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const overviewItems = adminNavigation.find(group => group.title === 'tx:nav.group.overview')?.items ?? []
|
||||||
|
expect(overviewItems.findIndex(item => item.name === '远程控制')).toBe(
|
||||||
|
overviewItems.findIndex(item => item.name === 'tx:nav.performanceAnalysis') + 1,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('builds admin navigation with dynamic module menu items sorted by menu order', () => {
|
it('builds admin navigation with dynamic module menu items sorted by menu order', () => {
|
||||||
@@ -133,13 +146,36 @@ describe('main layout navigation builder', () => {
|
|||||||
|
|
||||||
expect(buildBreadcrumbs({
|
expect(buildBreadcrumbs({
|
||||||
route: route('/dashboard/vscodex'),
|
route: route('/dashboard/vscodex'),
|
||||||
navigation,
|
navigation: buildNavigation({
|
||||||
modules: {},
|
canAccessAdmin: true,
|
||||||
|
modules: {
|
||||||
|
vscodex: {
|
||||||
|
active: true,
|
||||||
|
admin_route: '/dashboard/vscodex',
|
||||||
|
admin_menu_group: 'overview',
|
||||||
|
admin_menu_order: 80,
|
||||||
|
admin_menu_icon: 'SquareTerminal',
|
||||||
|
display_name: '远程控制',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
isModuleActive: () => false,
|
||||||
|
t: translate,
|
||||||
|
}),
|
||||||
|
modules: {
|
||||||
|
vscodex: {
|
||||||
|
active: true,
|
||||||
|
admin_route: '/dashboard/vscodex',
|
||||||
|
admin_menu_group: 'overview',
|
||||||
|
admin_menu_order: 80,
|
||||||
|
admin_menu_icon: 'SquareTerminal',
|
||||||
|
display_name: '远程控制',
|
||||||
|
},
|
||||||
|
},
|
||||||
isNavActive: href => href === '/dashboard/vscodex',
|
isNavActive: href => href === '/dashboard/vscodex',
|
||||||
t: translate,
|
t: translate,
|
||||||
})).toEqual([
|
})).toEqual([
|
||||||
expect.objectContaining({ label: expect.any(String) }),
|
expect.objectContaining({ label: expect.any(String) }),
|
||||||
{ label: 'tx:nav.vscodex' },
|
{ label: '远程控制' },
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const moduleIconMap: Record<string, LucideIcon> = {
|
|||||||
Server,
|
Server,
|
||||||
Send,
|
Send,
|
||||||
SlidersHorizontal,
|
SlidersHorizontal,
|
||||||
|
SquareTerminal,
|
||||||
CreditCard,
|
CreditCard,
|
||||||
Gift,
|
Gift,
|
||||||
}
|
}
|
||||||
@@ -117,12 +118,12 @@ export function buildNavigation(options: {
|
|||||||
title: t('nav.group.overview'),
|
title: t('nav.group.overview'),
|
||||||
items: [
|
items: [
|
||||||
{ name: t('nav.dashboard'), href: '/admin/dashboard', icon: Home },
|
{ name: t('nav.dashboard'), href: '/admin/dashboard', icon: Home },
|
||||||
{ name: t('nav.vscodex'), href: '/dashboard/vscodex', icon: SquareTerminal },
|
|
||||||
{ name: t('nav.operations'), href: '/admin/operations', icon: Activity },
|
{ name: t('nav.operations'), href: '/admin/operations', icon: Activity },
|
||||||
{ name: t('nav.healthMonitor'), href: '/admin/health-monitor', icon: Activity },
|
{ name: t('nav.healthMonitor'), href: '/admin/health-monitor', icon: Activity },
|
||||||
{ name: t('nav.userStats'), href: '/admin/user-stats', icon: BarChart3 },
|
{ name: t('nav.userStats'), href: '/admin/user-stats', icon: BarChart3 },
|
||||||
{ name: t('nav.costAnalysis'), href: '/admin/cost-analysis', icon: Gauge },
|
{ name: t('nav.costAnalysis'), href: '/admin/cost-analysis', icon: Gauge },
|
||||||
{ name: t('nav.performanceAnalysis'), href: '/admin/performance-analysis', icon: Activity },
|
{ name: t('nav.performanceAnalysis'), href: '/admin/performance-analysis', icon: Activity },
|
||||||
|
...activeModuleItems(modules, 'overview'),
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1138,6 +1138,20 @@ const MOCK_MODULE_DEFINITIONS: Array<Omit<ModuleStatus, 'active' | 'health'> & {
|
|||||||
admin_menu_group: null,
|
admin_menu_group: null,
|
||||||
admin_menu_order: 59,
|
admin_menu_order: 59,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'vscodex',
|
||||||
|
display_name: '远程控制',
|
||||||
|
description: '通过 aether-vscodex 插件远程控制本地 Codex 工作区',
|
||||||
|
category: 'integration',
|
||||||
|
available: true,
|
||||||
|
enabled: false,
|
||||||
|
config_validated: true,
|
||||||
|
config_error: null,
|
||||||
|
admin_route: '/dashboard/vscodex',
|
||||||
|
admin_menu_icon: 'SquareTerminal',
|
||||||
|
admin_menu_group: 'overview',
|
||||||
|
admin_menu_order: 80,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 's3_backup',
|
name: 's3_backup',
|
||||||
display_name: 'S3 备份',
|
display_name: 'S3 备份',
|
||||||
|
|||||||
Reference in New Issue
Block a user