@kai-chat/desktop 0.0.1
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/LICENSE +167 -0
- package/bin/kai-desktop.js +21 -0
- package/bin/launch.js +21 -0
- package/dist/electron/browser-window.js +133 -0
- package/dist/electron/capabilities/browser.js +479 -0
- package/dist/electron/capabilities/filesystem.js +91 -0
- package/dist/electron/capabilities/screen.js +29 -0
- package/dist/electron/claude.js +424 -0
- package/dist/electron/image.js +27 -0
- package/dist/electron/main.js +124 -0
- package/dist/electron/preload.js +43 -0
- package/dist/electron/settings.js +91 -0
- package/dist/renderer/assets/index-Cdc8zABU.js +260 -0
- package/dist/renderer/assets/index-I1Zu8bIR.css +1 -0
- package/dist/renderer/index.html +13 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
KAI-CHAT PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eutima AG, Switzerland. All rights reserved.
|
|
4
|
+
|
|
5
|
+
"Eutima AG" means Eutima AG, Switzerland.
|
|
6
|
+
|
|
7
|
+
"Software" means the KAI-Chat software package and all files included in
|
|
8
|
+
the package, whether in source, compiled, bundled, or other machine-readable
|
|
9
|
+
form.
|
|
10
|
+
|
|
11
|
+
"KAI-Chat Account" means a valid user account issued or authorized by
|
|
12
|
+
Eutima AG for use with a KAI-Chat server operated or authorized by Eutima AG.
|
|
13
|
+
|
|
14
|
+
1. GRANT OF LICENSE
|
|
15
|
+
|
|
16
|
+
Subject to the terms of this License, Eutima AG grants you a limited,
|
|
17
|
+
non-exclusive, non-transferable, non-sublicensable license to use the
|
|
18
|
+
Software solely as permitted by this License.
|
|
19
|
+
|
|
20
|
+
You may use the Software:
|
|
21
|
+
|
|
22
|
+
(a) if you hold a valid KAI-Chat Account, solely for running the Software
|
|
23
|
+
unmodified to connect to and interact with a KAI-Chat server operated or
|
|
24
|
+
authorized by Eutima AG; or
|
|
25
|
+
|
|
26
|
+
(b) if Eutima AG has separately authorized your use in writing, subject to
|
|
27
|
+
the terms of that authorization.
|
|
28
|
+
|
|
29
|
+
No other rights or licenses are granted.
|
|
30
|
+
|
|
31
|
+
2. RESTRICTIONS
|
|
32
|
+
|
|
33
|
+
Except where expressly permitted by this License or by mandatory applicable
|
|
34
|
+
law, you may not:
|
|
35
|
+
|
|
36
|
+
(a) modify, adapt, patch, translate, fork, or create derivative works of the
|
|
37
|
+
Software;
|
|
38
|
+
|
|
39
|
+
(b) reverse engineer, decompile, disassemble, deobfuscate, or otherwise
|
|
40
|
+
attempt to derive source code, algorithms, protocols, or data formats from
|
|
41
|
+
the Software;
|
|
42
|
+
|
|
43
|
+
(c) sell, rent, lease, sublicense, distribute, publish, transfer, or otherwise
|
|
44
|
+
make the Software available to any third party;
|
|
45
|
+
|
|
46
|
+
(d) remove, alter, or obscure copyright notices, license notices, or other
|
|
47
|
+
proprietary notices contained in the Software;
|
|
48
|
+
|
|
49
|
+
(e) use the Software to develop, operate, or provide a product or service that
|
|
50
|
+
substantially replicates KAI-Chat or is intended to compete with
|
|
51
|
+
KAI-Chat; or
|
|
52
|
+
|
|
53
|
+
(f) use the Software in a manner that circumvents authentication, access
|
|
54
|
+
controls, licensing mechanisms, usage restrictions, or other technical
|
|
55
|
+
protections implemented by Eutima AG.
|
|
56
|
+
|
|
57
|
+
Permission granted for one specific modification, use case, person, or
|
|
58
|
+
organization does not constitute permission for any other modification, use
|
|
59
|
+
case, person, or organization unless expressly agreed by Eutima AG in writing.
|
|
60
|
+
|
|
61
|
+
3. NO REDISTRIBUTION
|
|
62
|
+
|
|
63
|
+
The Software may be obtained from npm or other distribution channels expressly
|
|
64
|
+
authorized by Eutima AG.
|
|
65
|
+
|
|
66
|
+
Obtaining the Software from a public distribution channel does not grant any
|
|
67
|
+
right to redistribute, mirror, republish, sublicense, or otherwise make the
|
|
68
|
+
Software available outside those authorized channels.
|
|
69
|
+
|
|
70
|
+
4. OWNERSHIP
|
|
71
|
+
|
|
72
|
+
The Software is licensed, not sold.
|
|
73
|
+
|
|
74
|
+
Eutima AG and its licensors retain all right, title, and interest in and to
|
|
75
|
+
the Software, including all intellectual property rights.
|
|
76
|
+
|
|
77
|
+
Except for the limited license expressly granted under this License, no rights
|
|
78
|
+
are granted, whether express, implied, by estoppel, or otherwise.
|
|
79
|
+
|
|
80
|
+
5. TRADEMARKS
|
|
81
|
+
|
|
82
|
+
This License does not grant any rights to use the trademarks, trade names,
|
|
83
|
+
logos, service marks, or other branding of Eutima AG or KAI-Chat.
|
|
84
|
+
|
|
85
|
+
You may not use the name "KAI-Chat", the name "Eutima", or associated logos or
|
|
86
|
+
branding to imply endorsement, sponsorship, affiliation, or authorization
|
|
87
|
+
without prior written permission from Eutima AG.
|
|
88
|
+
|
|
89
|
+
6. THIRD-PARTY SOFTWARE
|
|
90
|
+
|
|
91
|
+
The Software may include or depend upon third-party software components that
|
|
92
|
+
are distributed under their own license terms.
|
|
93
|
+
|
|
94
|
+
Those components remain subject to their respective licenses. Nothing in this
|
|
95
|
+
License is intended to restrict rights granted to you under applicable
|
|
96
|
+
third-party licenses.
|
|
97
|
+
|
|
98
|
+
7. NO WARRANTY
|
|
99
|
+
|
|
100
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY
|
|
101
|
+
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
|
|
102
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND
|
|
103
|
+
NON-INFRINGEMENT, TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW.
|
|
104
|
+
|
|
105
|
+
8. LIMITATION OF LIABILITY
|
|
106
|
+
|
|
107
|
+
TO THE MAXIMUM EXTENT PERMITTED BY SWISS LAW, EUTIMA AG SHALL NOT BE LIABLE
|
|
108
|
+
FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, OR PUNITIVE DAMAGES,
|
|
109
|
+
OR FOR ANY LOSS OF PROFITS, REVENUE, DATA, BUSINESS, OR GOODWILL ARISING OUT
|
|
110
|
+
OF OR IN CONNECTION WITH THE SOFTWARE OR THIS LICENSE.
|
|
111
|
+
|
|
112
|
+
Nothing in this License excludes or limits liability to the extent that such
|
|
113
|
+
exclusion or limitation is prohibited by mandatory applicable law, including
|
|
114
|
+
liability arising from intentional misconduct or gross negligence where such
|
|
115
|
+
liability cannot lawfully be excluded.
|
|
116
|
+
|
|
117
|
+
9. TERMINATION
|
|
118
|
+
|
|
119
|
+
This License terminates automatically if you materially breach its terms.
|
|
120
|
+
|
|
121
|
+
Upon termination, you must immediately cease using the Software and delete all
|
|
122
|
+
copies of the Software in your possession or control, except where retention
|
|
123
|
+
is required by mandatory applicable law.
|
|
124
|
+
|
|
125
|
+
Termination does not affect any rights or remedies that Eutima AG may have
|
|
126
|
+
arising from a breach of this License.
|
|
127
|
+
|
|
128
|
+
10. CHANGES TO THIS LICENSE
|
|
129
|
+
|
|
130
|
+
Eutima AG may publish updated versions of this License for future versions or
|
|
131
|
+
releases of the Software.
|
|
132
|
+
|
|
133
|
+
Unless expressly stated otherwise, the license applicable to a particular
|
|
134
|
+
version of the Software is the license included with that version.
|
|
135
|
+
|
|
136
|
+
11. GOVERNING LAW AND JURISDICTION
|
|
137
|
+
|
|
138
|
+
This License is governed by Swiss substantive law, excluding its conflict of
|
|
139
|
+
laws rules.
|
|
140
|
+
|
|
141
|
+
The exclusive place of jurisdiction for disputes arising from or in connection
|
|
142
|
+
with this License shall be the competent courts at the registered seat of
|
|
143
|
+
Eutima AG, Switzerland, subject to mandatory statutory jurisdiction.
|
|
144
|
+
|
|
145
|
+
12. SEVERABILITY
|
|
146
|
+
|
|
147
|
+
If any provision of this License is held to be invalid or unenforceable, the
|
|
148
|
+
remaining provisions shall remain in full force and effect.
|
|
149
|
+
|
|
150
|
+
13. ENTIRE AGREEMENT
|
|
151
|
+
|
|
152
|
+
This License constitutes the entire agreement between you and Eutima AG
|
|
153
|
+
concerning the licensing of the Software, except for any separate written
|
|
154
|
+
agreement between you and Eutima AG that expressly applies to the Software.
|
|
155
|
+
|
|
156
|
+
Any separate written authorization or agreement with Eutima AG takes
|
|
157
|
+
precedence over this License to the extent of any conflict.
|
|
158
|
+
|
|
159
|
+
PERMISSION REQUESTS
|
|
160
|
+
|
|
161
|
+
Requests for additional permissions, including permission to modify,
|
|
162
|
+
redistribute, integrate, or otherwise use the Software outside the scope of
|
|
163
|
+
this License, should be directed to:
|
|
164
|
+
|
|
165
|
+
Eutima AG
|
|
166
|
+
Switzerland
|
|
167
|
+
info@eutima.ch
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { spawn } = require('child_process');
|
|
5
|
+
// Resolves to the path of the Electron binary that npm installed next to this package.
|
|
6
|
+
const electronPath = require('electron');
|
|
7
|
+
const { buildLaunch } = require('./launch.js');
|
|
8
|
+
|
|
9
|
+
const launch = buildLaunch(electronPath, process.argv.slice(2), process.env);
|
|
10
|
+
const child = spawn(launch.command, launch.args, { stdio: 'inherit', env: launch.env });
|
|
11
|
+
|
|
12
|
+
for (const signal of ['SIGINT', 'SIGTERM']) {
|
|
13
|
+
process.on(signal, () => child.kill(signal));
|
|
14
|
+
}
|
|
15
|
+
child.on('error', (err) => {
|
|
16
|
+
console.error(`Could not start KAI: ${err.message}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
|
19
|
+
child.on('exit', (code, signal) => {
|
|
20
|
+
process.exit(signal ? 1 : (code ?? 0));
|
|
21
|
+
});
|
package/bin/launch.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
/** The folder of this package: the Electron main process (`main` in package.json) lives under it. */
|
|
6
|
+
const packageRoot = path.resolve(__dirname, '..');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* What to run for `kai-desktop`: the Electron binary pointed at this package. NODE_ENV is forced
|
|
10
|
+
* to production because electron/main.ts otherwise loads the Vite dev server (localhost:5173)
|
|
11
|
+
* instead of the bundled renderer.
|
|
12
|
+
*/
|
|
13
|
+
function buildLaunch(electronPath, args, env) {
|
|
14
|
+
return {
|
|
15
|
+
command: electronPath,
|
|
16
|
+
args: [packageRoot, ...args],
|
|
17
|
+
env: { ...env, NODE_ENV: 'production' },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { buildLaunch, packageRoot };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.browser = void 0;
|
|
4
|
+
const electron_1 = require("electron");
|
|
5
|
+
const browser_js_1 = require("./capabilities/browser.js");
|
|
6
|
+
const image_js_1 = require("./image.js");
|
|
7
|
+
const settings_js_1 = require("./settings.js");
|
|
8
|
+
// The Electron side of the agent browser: a visible window, and the policy that keeps it where the
|
|
9
|
+
// person would expect. Only glue: what an agent may ask (`BrowserController`) and where the window
|
|
10
|
+
// may go (`checkUrl`) are decided, and tested, in `capabilities/browser.ts`. This file is checked by
|
|
11
|
+
// using it (see the manual steps in the change that added it).
|
|
12
|
+
const LOAD_TIMEOUT_MS = 30_000;
|
|
13
|
+
const SETTLE_MS = 5_000;
|
|
14
|
+
// What a page may fetch besides pages: pictures and the like can be inline data, everything else
|
|
15
|
+
// goes through the same address policy as the page itself.
|
|
16
|
+
const INLINE = new Set(['data:', 'blob:']);
|
|
17
|
+
function protect(ses, resolve) {
|
|
18
|
+
ses.webRequest.onBeforeRequest(async (details, callback) => {
|
|
19
|
+
try {
|
|
20
|
+
const kind = new URL(details.url).protocol;
|
|
21
|
+
if (INLINE.has(kind) && details.resourceType !== 'mainFrame' && details.resourceType !== 'subFrame')
|
|
22
|
+
return callback({});
|
|
23
|
+
await (0, browser_js_1.checkUrl)(details.url, resolve);
|
|
24
|
+
callback({});
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
callback({ cancel: true });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
// Nothing on a page may ask for the camera, the microphone, the location, notifications, ...
|
|
31
|
+
ses.setPermissionRequestHandler((_contents, _permission, callback) => callback(false));
|
|
32
|
+
ses.setPermissionCheckHandler(() => false);
|
|
33
|
+
ses.on('will-download', event => event.preventDefault());
|
|
34
|
+
}
|
|
35
|
+
function protectContents(contents) {
|
|
36
|
+
contents.setWindowOpenHandler(() => ({ action: 'deny' }));
|
|
37
|
+
contents.on('will-prevent-unload', event => event.preventDefault());
|
|
38
|
+
// Dialogs would block the whole app until someone answers them.
|
|
39
|
+
contents.on('dom-ready', () => {
|
|
40
|
+
void contents
|
|
41
|
+
.executeJavaScript('window.alert = () => {}; window.confirm = () => false; window.prompt = () => null;')
|
|
42
|
+
.catch(() => undefined);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function openWindow(resolve) {
|
|
46
|
+
// A partition without the "persist:" prefix lives in memory: no cookie, login or storage is
|
|
47
|
+
// shared with anything else, and all of it is gone when the window is.
|
|
48
|
+
const partition = `kai-browser-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
49
|
+
const ses = electron_1.session.fromPartition(partition);
|
|
50
|
+
protect(ses, resolve);
|
|
51
|
+
const win = new electron_1.BrowserWindow({
|
|
52
|
+
width: 1100,
|
|
53
|
+
height: 800,
|
|
54
|
+
title: 'KAI browser',
|
|
55
|
+
webPreferences: {
|
|
56
|
+
partition,
|
|
57
|
+
sandbox: true,
|
|
58
|
+
contextIsolation: true,
|
|
59
|
+
nodeIntegration: false,
|
|
60
|
+
webSecurity: true,
|
|
61
|
+
backgroundThrottling: false,
|
|
62
|
+
spellcheck: false,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
const contents = win.webContents;
|
|
66
|
+
protectContents(contents);
|
|
67
|
+
const closed = [];
|
|
68
|
+
win.on('closed', () => {
|
|
69
|
+
for (const callback of closed)
|
|
70
|
+
callback();
|
|
71
|
+
void ses.clearStorageData().catch(() => undefined);
|
|
72
|
+
});
|
|
73
|
+
const wait = (ms) => new Promise(done => setTimeout(done, ms));
|
|
74
|
+
return {
|
|
75
|
+
async load(url) {
|
|
76
|
+
let timer;
|
|
77
|
+
const timeout = new Promise((_, reject) => {
|
|
78
|
+
timer = setTimeout(() => {
|
|
79
|
+
contents.stop();
|
|
80
|
+
reject(new Error('The page took too long to load.'));
|
|
81
|
+
}, LOAD_TIMEOUT_MS);
|
|
82
|
+
});
|
|
83
|
+
try {
|
|
84
|
+
// An address that loads a download or is replaced by another navigation rejects with ERR_ABORTED.
|
|
85
|
+
await Promise.race([contents.loadURL(url).catch(err => (String(err?.message ?? err).includes('ERR_ABORTED') ? undefined : Promise.reject(err))), timeout]);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
if (timer)
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
url: () => contents.getURL(),
|
|
93
|
+
evaluate: (script) => contents.executeJavaScript(script, true),
|
|
94
|
+
async click(x, y) {
|
|
95
|
+
contents.sendInputEvent({ type: 'mouseMove', x, y });
|
|
96
|
+
contents.sendInputEvent({ type: 'mouseDown', x, y, button: 'left', clickCount: 1 });
|
|
97
|
+
contents.sendInputEvent({ type: 'mouseUp', x, y, button: 'left', clickCount: 1 });
|
|
98
|
+
},
|
|
99
|
+
insertText: text => contents.insertText(text),
|
|
100
|
+
async pressEnter() {
|
|
101
|
+
// Key events only reach a page that has the focus (mouse events do not need it).
|
|
102
|
+
contents.focus();
|
|
103
|
+
// The `char` event is what submits a form, and it needs the character itself, not the key's name.
|
|
104
|
+
contents.sendInputEvent({ type: 'keyDown', keyCode: 'Enter' });
|
|
105
|
+
contents.sendInputEvent({ type: 'char', keyCode: '\r' });
|
|
106
|
+
contents.sendInputEvent({ type: 'keyUp', keyCode: 'Enter' });
|
|
107
|
+
},
|
|
108
|
+
async settle() {
|
|
109
|
+
const started = Date.now();
|
|
110
|
+
await wait(300);
|
|
111
|
+
while (contents.isLoading() && Date.now() - started < SETTLE_MS)
|
|
112
|
+
await wait(100);
|
|
113
|
+
await wait(200);
|
|
114
|
+
},
|
|
115
|
+
async capture() {
|
|
116
|
+
return (await contents.capturePage()).toPNG();
|
|
117
|
+
},
|
|
118
|
+
close: () => {
|
|
119
|
+
if (!win.isDestroyed())
|
|
120
|
+
win.close();
|
|
121
|
+
},
|
|
122
|
+
onClosed: callback => void closed.push(callback),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const resolve = (0, browser_js_1.cachedResolver)(browser_js_1.systemResolver);
|
|
126
|
+
/** The one browser of this app. */
|
|
127
|
+
exports.browser = new browser_js_1.BrowserController({
|
|
128
|
+
enabled: settings_js_1.getBrowserEnabled,
|
|
129
|
+
open: () => openWindow(resolve),
|
|
130
|
+
resolve,
|
|
131
|
+
toImage: image_js_1.toImageResult,
|
|
132
|
+
});
|
|
133
|
+
//# sourceMappingURL=browser-window.js.map
|