@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 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