diff --git a/frontend/src/__tests__/meldwork/ConnectorPackagesPanel.spec.js b/frontend/src/__tests__/meldwork/ConnectorPackagesPanel.spec.js
index ae3c7be..414e760 100644
--- a/frontend/src/__tests__/meldwork/ConnectorPackagesPanel.spec.js
+++ b/frontend/src/__tests__/meldwork/ConnectorPackagesPanel.spec.js
@@ -60,4 +60,22 @@ describe('Connector package settings', () => {
expect(wrapper.text()).toContain(t('connectorPackages.instanceInUse'))
wrapper.unmount()
})
+
+ it('displays inspected permissions and durable trust history', async () => {
+ const item = { packageId: 'package', manifestId: 'manifest', state: 'imported', label: 'Example', connectorVersion: '1.0.0', publisher: { name: 'Publisher' } }
+ const api = { packages: vi.fn().mockResolvedValue([item]), list: vi.fn().mockResolvedValue([]),
+ inspect: vi.fn().mockResolvedValue({ packageId: 'package', origin: { filename: 'example.json', sha256: 'abc' }, manifest: {
+ transport: { type: 'http', protocol: 'json' }, permissionModes: ['read-only'],
+ credentials: { slots: [{ slotId: 'key' }] }, outboundDestinations: ['https://example.com'],
+ } }), audit: vi.fn().mockResolvedValue([{ eventId: 'event', action: 'imported', timestamp: '2026-11-06T00:00:00Z' }]) }
+ window.meldworkDesktop = { localAgentConnector: api }
+ const wrapper = mount(ConnectorPackagesPanel)
+ await flushPromises()
+ await wrapper.findAll('button').find(button => button.text() === t('connectorPackages.inspect')).trigger('click')
+ await flushPromises()
+ expect(wrapper.text()).toContain('read-only')
+ expect(wrapper.text()).toContain('https://example.com')
+ expect(wrapper.text()).toContain(t('connectorPackages.event.imported'))
+ wrapper.unmount()
+ })
})
diff --git a/frontend/src/components/ConnectorPackagesPanel.vue b/frontend/src/components/ConnectorPackagesPanel.vue
index 3a60323..0bb2f81 100644
--- a/frontend/src/components/ConnectorPackagesPanel.vue
+++ b/frontend/src/components/ConnectorPackagesPanel.vue
@@ -10,6 +10,7 @@
{{ item.label }} {{ item.connectorVersion }}{{ item.publisher.name }} · {{ t(`connectorPackages.state.${item.state}`) }}
+
@@ -18,6 +19,15 @@
+
+
{{ t('connectorPackages.origin') }}: {{ selected.origin.filename }} · {{ selected.origin.sha256 }}
+
{{ t('connectorPackages.transport') }}: {{ selected.manifest.transport.type }} / {{ selected.manifest.transport.protocol }}
+
{{ t('connectorPackages.permissions') }}: {{ selected.manifest.permissionModes.join(', ') || t('connectorPackages.none') }}
+
{{ t('connectorPackages.credentials') }}: {{ selected.manifest.credentials.slots.map(slot => slot.slotId).join(', ') || t('connectorPackages.none') }}
+
{{ t('connectorPackages.destinations') }}: {{ selected.manifest.outboundDestinations.join(', ') || t('connectorPackages.none') }}
+
{{ t('connectorPackages.audit') }}
+
- {{ t(`connectorPackages.event.${event.action}`) }} · {{ event.timestamp }}
+
{{ t('connectorPackages.instances') }}
@@ -49,6 +59,8 @@ const busy = ref(false)
const error = ref('')
const notice = ref('')
const confirmingInstance = ref('')
+const selected = ref(null)
+const audit = ref([])
async function run(action) {
busy.value = true
error.value = ''
@@ -58,7 +70,16 @@ async function run(action) {
finally { busy.value = false }
}
function importPackage() { return run(() => api.import()) }
-function perform(action, item) { return run(() => api[action](item.packageId)) }
+function perform(action, item) {
+ selected.value = null
+ return run(() => api[action](item.packageId))
+}
+function inspect(item) {
+ if (selected.value?.packageId === item.packageId) { selected.value = null; return }
+ return run(async () => {
+ ;[selected.value, audit.value] = await Promise.all([api.inspect(item.packageId), api.audit(item.packageId)])
+ })
+}
function testInstance(instance) {
return run(async () => {
const result = await api.test(instance.instanceId)
@@ -88,6 +109,8 @@ onMounted(() => run(async () => {}))
.connector-heading, .connector-row { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.connector-heading h2 { font-size: 18px; }
.connector-row { padding: 14px 0; border-bottom: 1px solid var(--border); }
+.connector-row:has(.connector-detail) { flex-wrap: wrap; }
+.connector-detail { flex-basis: 100%; overflow-wrap: anywhere; }
.connector-row p { color: var(--muted); margin: 4px 0 0; }
.connector-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.connector-actions button { border: 0; background: transparent; color: var(--accent-text); cursor: pointer; padding: 8px; }
diff --git a/frontend/src/locales/en.js b/frontend/src/locales/en.js
index 56bfaaf..2197710 100644
--- a/frontend/src/locales/en.js
+++ b/frontend/src/locales/en.js
@@ -20,6 +20,21 @@ export default {
'connectorPackages.delete': 'Delete instance',
'connectorPackages.deletePrompt': 'Delete this instance?',
'connectorPackages.instanceInUse': 'This instance is still used by a conversation. Remove it from the conversation first.',
+ 'connectorPackages.inspect': 'Details and history',
+ 'connectorPackages.origin': 'Origin and hash',
+ 'connectorPackages.transport': 'Transport',
+ 'connectorPackages.permissions': 'Permissions',
+ 'connectorPackages.credentials': 'Credential slots',
+ 'connectorPackages.destinations': 'Outbound destinations',
+ 'connectorPackages.none': 'None',
+ 'connectorPackages.audit': 'Trust history',
+ 'connectorPackages.event.imported': 'Imported',
+ 'connectorPackages.event.approved': 'Approved',
+ 'connectorPackages.event.installed': 'Installed',
+ 'connectorPackages.event.disabled': 'Disabled',
+ 'connectorPackages.event.revoked': 'Trust revoked',
+ 'connectorPackages.event.upgraded': 'Upgraded',
+ 'connectorPackages.event.removed': 'Removed',
'connectorPackages.state.imported': 'Awaiting approval',
'connectorPackages.state.approved': 'Approved',
'connectorPackages.state.installed': 'Installed',
diff --git a/frontend/src/locales/zh.js b/frontend/src/locales/zh.js
index d32ce20..f83a8d5 100644
--- a/frontend/src/locales/zh.js
+++ b/frontend/src/locales/zh.js
@@ -20,6 +20,21 @@ export default {
'connectorPackages.delete': '删除实例',
'connectorPackages.deletePrompt': '确定删除此实例?',
'connectorPackages.instanceInUse': '此实例仍被会话使用,请先从会话中移除。',
+ 'connectorPackages.inspect': '查看详情和记录',
+ 'connectorPackages.origin': '来源与哈希',
+ 'connectorPackages.transport': '传输方式',
+ 'connectorPackages.permissions': '权限',
+ 'connectorPackages.credentials': '凭据槽',
+ 'connectorPackages.destinations': '外联目标',
+ 'connectorPackages.none': '无',
+ 'connectorPackages.audit': '信任记录',
+ 'connectorPackages.event.imported': '已导入',
+ 'connectorPackages.event.approved': '已批准',
+ 'connectorPackages.event.installed': '已安装',
+ 'connectorPackages.event.disabled': '已停用',
+ 'connectorPackages.event.revoked': '已撤销信任',
+ 'connectorPackages.event.upgraded': '已升级',
+ 'connectorPackages.event.removed': '已移除',
'connectorPackages.state.imported': '待批准',
'connectorPackages.state.approved': '已批准',
'connectorPackages.state.installed': '已安装',