@aiwg/cockpit 2026.8.3 → 2026.8.5
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/bridge/src/server.mjs +388 -18
- package/contrib/contribution.schema.json +2 -2
- package/package.json +1 -1
- package/shell-core/keychain.mjs +82 -63
- package/web/dist/assets/index-C5N6qGZf.js +312 -0
- package/web/dist/assets/{index-CQkRFleq.css → index-CLv_bmPX.css} +1 -1
- package/web/dist/index.html +2 -2
- package/web/src/App.test.tsx +19 -1
- package/web/src/App.tsx +4 -1
- package/web/src/api.ts +7 -1
- package/web/src/components/Actions.tsx +1 -0
- package/web/src/components/Activity.test.tsx +38 -0
- package/web/src/components/Activity.tsx +78 -0
- package/web/src/components/CapabilitySearch.tsx +5 -4
- package/web/src/components/Explore.tsx +45 -5
- package/web/src/components/Inventory.test.tsx +12 -0
- package/web/src/components/Inventory.tsx +7 -0
- package/web/src/components/LaunchInstanceModal.test.tsx +17 -0
- package/web/src/components/Missions.tsx +89 -4
- package/web/src/styles.css +8 -0
- package/web/src/types.ts +17 -3
- package/web/dist/assets/index-Dj3dFeFT.js +0 -312
package/shell-core/keychain.mjs
CHANGED
|
@@ -3,7 +3,6 @@ import { platform } from 'node:os';
|
|
|
3
3
|
|
|
4
4
|
const SERVICE = 'aiwg-cockpit-bridge';
|
|
5
5
|
const FOLDER = 'AIWG Cockpit';
|
|
6
|
-
const WALLET = process.env.AIWG_COCKPIT_KWALLET || 'kdewallet';
|
|
7
6
|
|
|
8
7
|
function collect(cmd, args, input, timeoutMs = 2_000) {
|
|
9
8
|
return new Promise((resolve, reject) => {
|
|
@@ -37,74 +36,94 @@ async function canRun(cmd) {
|
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Build the cross-platform keychain adapter around injectable platform and
|
|
41
|
+
* process seams. Production uses the defaults below; tests exercise the exact
|
|
42
|
+
* argv/stdin contract for every OS without pretending the CI host is macOS or
|
|
43
|
+
* Windows.
|
|
44
|
+
*/
|
|
45
|
+
export function createKeychainAdapter({
|
|
46
|
+
os = platform(),
|
|
47
|
+
env = process.env,
|
|
48
|
+
run = collect,
|
|
49
|
+
commandAvailable = canRun,
|
|
50
|
+
} = {}) {
|
|
51
|
+
const wallet = env.AIWG_COCKPIT_KWALLET || 'kdewallet';
|
|
45
52
|
|
|
46
|
-
async function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
'
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
async function windowsPowerShell() {
|
|
54
|
+
if (await commandAvailable('powershell')) return 'powershell';
|
|
55
|
+
if (await commandAvailable('pwsh')) return 'pwsh';
|
|
56
|
+
throw new Error('no supported Windows PowerShell command found');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function storeWindowsCredential(token, account) {
|
|
60
|
+
const ps = await windowsPowerShell();
|
|
61
|
+
const script = [
|
|
62
|
+
'[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]',
|
|
63
|
+
'$vault = New-Object Windows.Security.Credentials.PasswordVault',
|
|
64
|
+
'try { $vault.Remove($vault.Retrieve($args[0], $args[1])) } catch {}',
|
|
65
|
+
'$password = [Console]::In.ReadToEnd()',
|
|
66
|
+
'$credential = New-Object Windows.Security.Credentials.PasswordCredential -ArgumentList $args[0], $args[1], $password',
|
|
67
|
+
'$vault.Add($credential)',
|
|
68
|
+
].join('; ');
|
|
69
|
+
await run(ps, ['-NoProfile', '-NonInteractive', '-Command', script, SERVICE, account], token);
|
|
70
|
+
return { backend: 'windows-credential-manager', service: SERVICE, account, target: `${SERVICE}:${account}` };
|
|
71
|
+
}
|
|
59
72
|
|
|
60
|
-
async function readWindowsCredential(ref) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
async function readWindowsCredential(ref) {
|
|
74
|
+
const ps = await windowsPowerShell();
|
|
75
|
+
const script = [
|
|
76
|
+
'[void][Windows.Security.Credentials.PasswordVault,Windows.Security.Credentials,ContentType=WindowsRuntime]',
|
|
77
|
+
'$vault = New-Object Windows.Security.Credentials.PasswordVault',
|
|
78
|
+
'$credential = $vault.Retrieve($args[0], $args[1])',
|
|
79
|
+
'$credential.RetrievePassword()',
|
|
80
|
+
'[Console]::Out.Write($credential.Password)',
|
|
81
|
+
].join('; ');
|
|
82
|
+
return (await run(ps, ['-NoProfile', '-NonInteractive', '-Command', script, ref.service || SERVICE, ref.account])).trim();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
async store(token, account = `bridge-${process.pid}`) {
|
|
87
|
+
if (env.AIWG_COCKPIT_KEYCHAIN_DISABLED === '1') {
|
|
88
|
+
throw new Error('OS keychain disabled by AIWG_COCKPIT_KEYCHAIN_DISABLED');
|
|
89
|
+
}
|
|
90
|
+
if (os === 'darwin' && await commandAvailable('security')) {
|
|
91
|
+
await run('security', ['add-generic-password', '-a', account, '-s', SERVICE, '-w', token, '-U']);
|
|
92
|
+
return { backend: 'macos-keychain', service: SERVICE, account };
|
|
93
|
+
}
|
|
94
|
+
if (os === 'win32') return storeWindowsCredential(token, account);
|
|
95
|
+
if (await commandAvailable('secret-tool')) {
|
|
96
|
+
await run('secret-tool', ['store', '--label', 'AIWG Cockpit Bridge', 'service', SERVICE, 'account', account], token);
|
|
97
|
+
return { backend: 'libsecret', service: SERVICE, account };
|
|
98
|
+
}
|
|
99
|
+
if (env.AIWG_COCKPIT_ENABLE_KWALLET === '1' && await commandAvailable('kwallet-query')) {
|
|
100
|
+
await run('kwallet-query', ['-f', FOLDER, '-w', account, wallet], token);
|
|
101
|
+
return { backend: 'kwallet', service: SERVICE, account, wallet, folder: FOLDER };
|
|
102
|
+
}
|
|
103
|
+
throw new Error('no supported OS keychain command found');
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
async read(ref) {
|
|
107
|
+
if (!ref || typeof ref !== 'object') throw new Error('missing keychain reference');
|
|
108
|
+
if (ref.backend === 'macos-keychain') {
|
|
109
|
+
return (await run('security', ['find-generic-password', '-a', ref.account, '-s', ref.service || SERVICE, '-w'])).trim();
|
|
110
|
+
}
|
|
111
|
+
if (ref.backend === 'windows-credential-manager') return readWindowsCredential(ref);
|
|
112
|
+
if (ref.backend === 'libsecret') {
|
|
113
|
+
return (await run('secret-tool', ['lookup', 'service', ref.service || SERVICE, 'account', ref.account])).trim();
|
|
114
|
+
}
|
|
115
|
+
if (ref.backend === 'kwallet') {
|
|
116
|
+
return (await run('kwallet-query', ['-f', ref.folder || FOLDER, '-r', ref.account, ref.wallet || wallet])).trim();
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`unsupported keychain backend: ${ref.backend}`);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
70
121
|
}
|
|
71
122
|
|
|
72
123
|
export async function storeCockpitToken(token, account = `bridge-${process.pid}`) {
|
|
73
|
-
|
|
74
|
-
throw new Error('OS keychain disabled by AIWG_COCKPIT_KEYCHAIN_DISABLED');
|
|
75
|
-
}
|
|
76
|
-
const os = platform();
|
|
77
|
-
if (os === 'darwin' && await canRun('security')) {
|
|
78
|
-
await collect('security', ['add-generic-password', '-a', account, '-s', SERVICE, '-w', token, '-U']);
|
|
79
|
-
return { backend: 'macos-keychain', service: SERVICE, account };
|
|
80
|
-
}
|
|
81
|
-
if (os === 'win32') {
|
|
82
|
-
return storeWindowsCredential(token, account);
|
|
83
|
-
}
|
|
84
|
-
if (await canRun('secret-tool')) {
|
|
85
|
-
await collect('secret-tool', ['store', '--label', 'AIWG Cockpit Bridge', 'service', SERVICE, 'account', account], token);
|
|
86
|
-
return { backend: 'libsecret', service: SERVICE, account };
|
|
87
|
-
}
|
|
88
|
-
if (process.env.AIWG_COCKPIT_ENABLE_KWALLET === '1' && await canRun('kwallet-query')) {
|
|
89
|
-
await collect('kwallet-query', ['-f', FOLDER, '-w', account, WALLET], token);
|
|
90
|
-
return { backend: 'kwallet', service: SERVICE, account, wallet: WALLET, folder: FOLDER };
|
|
91
|
-
}
|
|
92
|
-
throw new Error('no supported OS keychain command found');
|
|
124
|
+
return createKeychainAdapter().store(token, account);
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
export async function readCockpitToken(ref) {
|
|
96
|
-
|
|
97
|
-
if (ref.backend === 'macos-keychain') {
|
|
98
|
-
return (await collect('security', ['find-generic-password', '-a', ref.account, '-s', ref.service || SERVICE, '-w'])).trim();
|
|
99
|
-
}
|
|
100
|
-
if (ref.backend === 'windows-credential-manager') {
|
|
101
|
-
return readWindowsCredential(ref);
|
|
102
|
-
}
|
|
103
|
-
if (ref.backend === 'libsecret') {
|
|
104
|
-
return (await collect('secret-tool', ['lookup', 'service', ref.service || SERVICE, 'account', ref.account])).trim();
|
|
105
|
-
}
|
|
106
|
-
if (ref.backend === 'kwallet') {
|
|
107
|
-
return (await collect('kwallet-query', ['-f', ref.folder || FOLDER, '-r', ref.account, ref.wallet || WALLET])).trim();
|
|
108
|
-
}
|
|
109
|
-
throw new Error(`unsupported keychain backend: ${ref.backend}`);
|
|
128
|
+
return createKeychainAdapter().read(ref);
|
|
110
129
|
}
|