@deepseekharness-mobile/companion 0.0.0-stage → 0.1.0
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 +21 -0
- package/README.md +25 -2
- package/cordis.patch.yml +20 -0
- package/dist/core/devices.d.ts +35 -0
- package/dist/core/devices.d.ts.map +1 -0
- package/dist/core/devices.js +90 -0
- package/dist/core/devices.js.map +1 -0
- package/dist/core/pairing.d.ts +21 -0
- package/dist/core/pairing.d.ts.map +1 -0
- package/dist/core/pairing.js +74 -0
- package/dist/core/pairing.js.map +1 -0
- package/dist/dsh/errors.d.ts +3 -0
- package/dist/dsh/errors.d.ts.map +1 -0
- package/dist/dsh/errors.js +15 -0
- package/dist/dsh/errors.js.map +1 -0
- package/dist/dsh/lifecycle.d.ts +37 -0
- package/dist/dsh/lifecycle.d.ts.map +1 -0
- package/dist/dsh/lifecycle.js +95 -0
- package/dist/dsh/lifecycle.js.map +1 -0
- package/dist/dsh/models.d.ts +39 -0
- package/dist/dsh/models.d.ts.map +1 -0
- package/dist/dsh/models.js +171 -0
- package/dist/dsh/models.js.map +1 -0
- package/dist/dsh/prompt.d.ts +8 -0
- package/dist/dsh/prompt.d.ts.map +1 -0
- package/dist/dsh/prompt.js +25 -0
- package/dist/dsh/prompt.js.map +1 -0
- package/dist/dsh/providers.d.ts +25 -0
- package/dist/dsh/providers.d.ts.map +1 -0
- package/dist/dsh/providers.js +315 -0
- package/dist/dsh/providers.js.map +1 -0
- package/dist/dsh/question-bridge.d.ts +25 -0
- package/dist/dsh/question-bridge.d.ts.map +1 -0
- package/dist/dsh/question-bridge.js +140 -0
- package/dist/dsh/question-bridge.js.map +1 -0
- package/dist/dsh/questions.d.ts +15 -0
- package/dist/dsh/questions.d.ts.map +1 -0
- package/dist/dsh/questions.js +126 -0
- package/dist/dsh/questions.js.map +1 -0
- package/dist/dsh/session-activity.d.ts +5 -0
- package/dist/dsh/session-activity.d.ts.map +1 -0
- package/dist/dsh/session-activity.js +100 -0
- package/dist/dsh/session-activity.js.map +1 -0
- package/dist/dsh/session-source.d.ts +19 -0
- package/dist/dsh/session-source.d.ts.map +1 -0
- package/dist/dsh/session-source.js +104 -0
- package/dist/dsh/session-source.js.map +1 -0
- package/dist/dsh/sessions.d.ts +10 -0
- package/dist/dsh/sessions.d.ts.map +1 -0
- package/dist/dsh/sessions.js +51 -0
- package/dist/dsh/sessions.js.map +1 -0
- package/dist/dsh/slash-commands.d.ts +15 -0
- package/dist/dsh/slash-commands.d.ts.map +1 -0
- package/dist/dsh/slash-commands.js +60 -0
- package/dist/dsh/slash-commands.js.map +1 -0
- package/dist/dsh/transcript.d.ts +28 -0
- package/dist/dsh/transcript.d.ts.map +1 -0
- package/dist/dsh/transcript.js +211 -0
- package/dist/dsh/transcript.js.map +1 -0
- package/dist/dsh/workspaces.d.ts +20 -0
- package/dist/dsh/workspaces.d.ts.map +1 -0
- package/dist/dsh/workspaces.js +123 -0
- package/dist/dsh/workspaces.js.map +1 -0
- package/dist/host-api.d.ts +148 -0
- package/dist/host-api.d.ts.map +1 -0
- package/dist/host-api.js +54 -0
- package/dist/host-api.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +268 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +5 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +40 -0
- package/dist/log.js.map +1 -0
- package/dist/open-browser.d.ts +3 -0
- package/dist/open-browser.d.ts.map +1 -0
- package/dist/open-browser.js +46 -0
- package/dist/open-browser.js.map +1 -0
- package/dist/pairing-message.d.ts +16 -0
- package/dist/pairing-message.d.ts.map +1 -0
- package/dist/pairing-message.js +38 -0
- package/dist/pairing-message.js.map +1 -0
- package/dist/persist/devices-file.d.ts +8 -0
- package/dist/persist/devices-file.d.ts.map +1 -0
- package/dist/persist/devices-file.js +47 -0
- package/dist/persist/devices-file.js.map +1 -0
- package/dist/probe.d.ts +25 -0
- package/dist/probe.d.ts.map +1 -0
- package/dist/probe.js +143 -0
- package/dist/probe.js.map +1 -0
- package/dist/public-origin.d.ts +11 -0
- package/dist/public-origin.d.ts.map +1 -0
- package/dist/public-origin.js +79 -0
- package/dist/public-origin.js.map +1 -0
- package/dist/qr-text.d.ts +2 -0
- package/dist/qr-text.d.ts.map +1 -0
- package/dist/qr-text.js +31 -0
- package/dist/qr-text.js.map +1 -0
- package/dist/routes/origin.d.ts +17 -0
- package/dist/routes/origin.d.ts.map +1 -0
- package/dist/routes/origin.js +85 -0
- package/dist/routes/origin.js.map +1 -0
- package/dist/routes/static.d.ts +9 -0
- package/dist/routes/static.d.ts.map +1 -0
- package/dist/routes/static.js +129 -0
- package/dist/routes/static.js.map +1 -0
- package/dist/routes/web-root.d.ts +2 -0
- package/dist/routes/web-root.d.ts.map +1 -0
- package/dist/routes/web-root.js +21 -0
- package/dist/routes/web-root.js.map +1 -0
- package/dist/server.d.ts +140 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +829 -0
- package/dist/server.js.map +1 -0
- package/dist/web/apple-touch-icon-precomposed.png +0 -0
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/assets/index-NOGXMIsS.js +65 -0
- package/dist/web/assets/index-NOGXMIsS.js.map +1 -0
- package/dist/web/assets/index-s7zoNGmx.css +1 -0
- package/dist/web/fonts/Montserrat-OFL.txt +93 -0
- package/dist/web/fonts/montserrat-medium.woff2 +0 -0
- package/dist/web/fonts/montserrat-regular.woff2 +0 -0
- package/dist/web/icon-192.png +0 -0
- package/dist/web/icon-512.png +0 -0
- package/dist/web/icon-maskable-512.png +0 -0
- package/dist/web/icon-maskable.svg +15 -0
- package/dist/web/icon.svg +6 -0
- package/dist/web/index.html +25 -0
- package/dist/web/manifest.webmanifest +37 -0
- package/package.json +60 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 joseph12n
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @deepseekharness-mobile/companion
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) host plugin for **DSH
|
|
4
|
+
Mobile**: a WebSocket bridge on port 6768, QR and device-token pairing, the embedded PWA and the
|
|
5
|
+
session/transcript/question/command/provider/device features described in the
|
|
6
|
+
[root README](https://github.com/joseph12n/dsh-mobile).
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
dsh profile add @deepseekharness-mobile/companion
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The profile reads the bundle's `cordis.patch.yml` and mounts the plugin. Restart DSH after the
|
|
15
|
+
install; the PWA is served at `/` and pairing at `/pair`. For the tailnet and phone steps, see
|
|
16
|
+
[docs/phone-setup.md](https://github.com/joseph12n/dsh-mobile/blob/main/docs/phone-setup.md).
|
|
17
|
+
|
|
18
|
+
## Security
|
|
19
|
+
|
|
20
|
+
The listener binds loopback by default and a phone must pair before any frame is accepted.
|
|
21
|
+
Device tokens can be revoked from the phone or from the host devices file. Do not expose the
|
|
22
|
+
listener directly to the internet; use the tailnet or a reverse proxy with TLS.
|
|
23
|
+
|
|
24
|
+
## License
|
|
25
|
+
|
|
26
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Capa de patch del plugin DSH Mobile.
|
|
2
|
+
#
|
|
3
|
+
# Se enlaza desde el perfil con `link:` en package.json y se activa añadiendo el
|
|
4
|
+
# bundle a `dsh.profile.bundles`, o bien insertando esta entrada en el
|
|
5
|
+
# `cordis.patch.yml` del perfil:
|
|
6
|
+
#
|
|
7
|
+
# - insert:
|
|
8
|
+
# - id: dsh-mobile-companion
|
|
9
|
+
# name: '@deepseekharness-mobile/companion'
|
|
10
|
+
# config: {}
|
|
11
|
+
#
|
|
12
|
+
# El plugin escucha en 127.0.0.1:6768 (loopback fijo: no hay modo de red). La
|
|
13
|
+
# autenticación por dispositivo ya existe; para llegar desde un teléfono se
|
|
14
|
+
# publica por el nombre del tailnet con `tailscale serve`, que sí alcanza el
|
|
15
|
+
# loopback y aporta HTTPS. La LAN directa no está soportada.
|
|
16
|
+
|
|
17
|
+
- insert:
|
|
18
|
+
- id: dsh-mobile-companion
|
|
19
|
+
name: './dist/index.js'
|
|
20
|
+
config: {}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface Device {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
platform: string;
|
|
5
|
+
createdAt: number;
|
|
6
|
+
lastSeenAt: number;
|
|
7
|
+
}
|
|
8
|
+
export interface StoredDevice extends Device {
|
|
9
|
+
tokenHash: string;
|
|
10
|
+
}
|
|
11
|
+
export interface DevicePersistence {
|
|
12
|
+
load(): StoredDevice[];
|
|
13
|
+
save(devices: StoredDevice[]): void;
|
|
14
|
+
}
|
|
15
|
+
export interface DeviceRegistryOptions {
|
|
16
|
+
persistence: DevicePersistence;
|
|
17
|
+
now?: () => number;
|
|
18
|
+
generateToken?: () => string;
|
|
19
|
+
generateId?: () => string;
|
|
20
|
+
}
|
|
21
|
+
export interface Enrolled {
|
|
22
|
+
device: Device;
|
|
23
|
+
token: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function hashToken(token: string): string;
|
|
26
|
+
export declare class DeviceRegistry {
|
|
27
|
+
#private;
|
|
28
|
+
constructor(options: DeviceRegistryOptions);
|
|
29
|
+
enroll(name: string, platform: string): Enrolled;
|
|
30
|
+
authenticate(token: string): Device | undefined;
|
|
31
|
+
flush(): void;
|
|
32
|
+
list(): Device[];
|
|
33
|
+
revoke(id: string): boolean;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=devices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../../src/core/devices.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAa,SAAQ,MAAM;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,IAAI,YAAY,EAAE,CAAC;IACvB,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,qBAAa,cAAc;;gBAQb,OAAO,EAAE,qBAAqB;IAgB1C,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,QAAQ;IAkBhD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAW/C,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,MAAM,EAAE;IAIhB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;CAc5B"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
export function hashToken(token) {
|
|
3
|
+
return createHash('sha256').update(token, 'utf8').digest('hex');
|
|
4
|
+
}
|
|
5
|
+
export class DeviceRegistry {
|
|
6
|
+
#persistence;
|
|
7
|
+
#now;
|
|
8
|
+
#generateToken;
|
|
9
|
+
#generateId;
|
|
10
|
+
#devices;
|
|
11
|
+
#dirty = false;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.#persistence = options.persistence;
|
|
14
|
+
this.#now = options.now ?? (() => Date.now());
|
|
15
|
+
this.#generateToken = options.generateToken ?? (() => randomBytes(32).toString('base64url'));
|
|
16
|
+
this.#generateId = options.generateId ?? (() => randomUUID());
|
|
17
|
+
this.#devices = this.#loadSafely();
|
|
18
|
+
}
|
|
19
|
+
#loadSafely() {
|
|
20
|
+
try {
|
|
21
|
+
return this.#persistence.load();
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
enroll(name, platform) {
|
|
28
|
+
const now = this.#now();
|
|
29
|
+
const token = this.#generateToken();
|
|
30
|
+
const stored = {
|
|
31
|
+
id: this.#generateId(),
|
|
32
|
+
name: name.trim().slice(0, 64) || 'Device',
|
|
33
|
+
platform: platform.slice(0, 64),
|
|
34
|
+
createdAt: now,
|
|
35
|
+
lastSeenAt: now,
|
|
36
|
+
tokenHash: hashToken(token),
|
|
37
|
+
};
|
|
38
|
+
this.#devices = [...this.#devices, stored];
|
|
39
|
+
this.#persist();
|
|
40
|
+
return { device: toPublic(stored), token };
|
|
41
|
+
}
|
|
42
|
+
authenticate(token) {
|
|
43
|
+
if (!token)
|
|
44
|
+
return undefined;
|
|
45
|
+
const hash = hashToken(token);
|
|
46
|
+
const found = this.#devices.find((device) => safeEqual(device.tokenHash, hash));
|
|
47
|
+
if (!found)
|
|
48
|
+
return undefined;
|
|
49
|
+
found.lastSeenAt = this.#now();
|
|
50
|
+
this.#dirty = true;
|
|
51
|
+
return toPublic(found);
|
|
52
|
+
}
|
|
53
|
+
flush() {
|
|
54
|
+
if (!this.#dirty)
|
|
55
|
+
return;
|
|
56
|
+
this.#persist();
|
|
57
|
+
}
|
|
58
|
+
list() {
|
|
59
|
+
return this.#devices.map(toPublic);
|
|
60
|
+
}
|
|
61
|
+
revoke(id) {
|
|
62
|
+
const before = this.#devices.length;
|
|
63
|
+
this.#devices = this.#devices.filter((device) => device.id !== id);
|
|
64
|
+
if (this.#devices.length === before)
|
|
65
|
+
return false;
|
|
66
|
+
this.#persist();
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
#persist() {
|
|
70
|
+
try {
|
|
71
|
+
this.#persistence.save(this.#devices);
|
|
72
|
+
this.#dirty = false;
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function toPublic(stored) {
|
|
78
|
+
const { tokenHash: _ignored, ...device } = stored;
|
|
79
|
+
return device;
|
|
80
|
+
}
|
|
81
|
+
function safeEqual(a, b) {
|
|
82
|
+
if (a.length !== b.length)
|
|
83
|
+
return false;
|
|
84
|
+
let diff = 0;
|
|
85
|
+
for (let index = 0; index < a.length; index += 1) {
|
|
86
|
+
diff |= a.charCodeAt(index) ^ b.charCodeAt(index);
|
|
87
|
+
}
|
|
88
|
+
return diff === 0;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devices.js","sourceRoot":"","sources":["../../src/core/devices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const CODE_BYTES = 16;
|
|
2
|
+
export declare const DEFAULT_TTL_MS: number;
|
|
3
|
+
export declare function normalizeCode(raw: string): string;
|
|
4
|
+
export interface IssuedCode {
|
|
5
|
+
code: string;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
}
|
|
8
|
+
export type RedeemResult = 'ok' | 'unknown' | 'expired' | 'consumed';
|
|
9
|
+
export interface PairingOptions {
|
|
10
|
+
ttlMs?: number;
|
|
11
|
+
now?: () => number;
|
|
12
|
+
generate?: () => string;
|
|
13
|
+
}
|
|
14
|
+
export declare class PairingService {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(options?: PairingOptions);
|
|
17
|
+
issue(): IssuedCode;
|
|
18
|
+
redeem(raw: string): RedeemResult;
|
|
19
|
+
pending(): IssuedCode[];
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=pairing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/core/pairing.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,eAAO,MAAM,cAAc,QAAiB,CAAC;AAmB7C,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAErE,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;CACzB;AAOD,qBAAa,cAAc;;gBAMb,OAAO,GAAE,cAAmB;IAMxC,KAAK,IAAI,UAAU;IAQnB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY;IAejC,OAAO,IAAI,UAAU,EAAE;CAcxB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
const ALPHABET = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
|
3
|
+
export const CODE_BYTES = 16;
|
|
4
|
+
export const DEFAULT_TTL_MS = 10 * 60 * 1000;
|
|
5
|
+
function encodeBase32(bytes) {
|
|
6
|
+
let out = '';
|
|
7
|
+
let buffer = 0;
|
|
8
|
+
let bits = 0;
|
|
9
|
+
for (const byte of bytes) {
|
|
10
|
+
buffer = (buffer << 8) | byte;
|
|
11
|
+
bits += 8;
|
|
12
|
+
while (bits >= 5) {
|
|
13
|
+
bits -= 5;
|
|
14
|
+
out += ALPHABET[(buffer >>> bits) & 31];
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (bits > 0)
|
|
18
|
+
out += ALPHABET[(buffer << (5 - bits)) & 31];
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
export function normalizeCode(raw) {
|
|
22
|
+
return raw
|
|
23
|
+
.toUpperCase()
|
|
24
|
+
.replace(/[^0-9A-Z]/g, '')
|
|
25
|
+
.replace(/[IL]/g, '1')
|
|
26
|
+
.replace(/O/g, '0');
|
|
27
|
+
}
|
|
28
|
+
export class PairingService {
|
|
29
|
+
#ttlMs;
|
|
30
|
+
#now;
|
|
31
|
+
#generate;
|
|
32
|
+
#codes = new Map();
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
this.#ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
35
|
+
this.#now = options.now ?? (() => Date.now());
|
|
36
|
+
this.#generate = options.generate ?? (() => encodeBase32(randomBytes(CODE_BYTES)));
|
|
37
|
+
}
|
|
38
|
+
issue() {
|
|
39
|
+
this.#prune();
|
|
40
|
+
const code = normalizeCode(this.#generate());
|
|
41
|
+
const expiresAt = this.#now() + this.#ttlMs;
|
|
42
|
+
this.#codes.set(code, { expiresAt, consumed: false });
|
|
43
|
+
return { code, expiresAt };
|
|
44
|
+
}
|
|
45
|
+
redeem(raw) {
|
|
46
|
+
const code = normalizeCode(raw);
|
|
47
|
+
const entry = this.#codes.get(code);
|
|
48
|
+
if (!entry)
|
|
49
|
+
return 'unknown';
|
|
50
|
+
if (entry.consumed)
|
|
51
|
+
return 'consumed';
|
|
52
|
+
if (entry.expiresAt <= this.#now()) {
|
|
53
|
+
this.#codes.delete(code);
|
|
54
|
+
return 'expired';
|
|
55
|
+
}
|
|
56
|
+
entry.consumed = true;
|
|
57
|
+
return 'ok';
|
|
58
|
+
}
|
|
59
|
+
pending() {
|
|
60
|
+
this.#prune();
|
|
61
|
+
const now = this.#now();
|
|
62
|
+
return [...this.#codes.entries()]
|
|
63
|
+
.filter(([, entry]) => !entry.consumed && entry.expiresAt > now)
|
|
64
|
+
.map(([code, entry]) => ({ code, expiresAt: entry.expiresAt }));
|
|
65
|
+
}
|
|
66
|
+
#prune() {
|
|
67
|
+
const horizon = this.#now() - 60 * 60 * 1000;
|
|
68
|
+
for (const [code, entry] of this.#codes) {
|
|
69
|
+
if (entry.expiresAt < horizon)
|
|
70
|
+
this.#codes.delete(code);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=pairing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.js","sourceRoot":"","sources":["../../src/core/pairing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/dsh/errors.ts"],"names":[],"mappings":"AAKA,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIzD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMhD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function codeOf(error) {
|
|
2
|
+
if (typeof error !== 'object' || error === null)
|
|
3
|
+
return undefined;
|
|
4
|
+
const { code } = error;
|
|
5
|
+
return typeof code === 'string' ? code : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function messageOf(error) {
|
|
8
|
+
if (typeof error === 'object' && error !== null) {
|
|
9
|
+
const { message } = error;
|
|
10
|
+
if (typeof message === 'string')
|
|
11
|
+
return message;
|
|
12
|
+
}
|
|
13
|
+
return String(error);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/dsh/errors.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAClE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAmB,CAAC;IACrC,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,EAAE,OAAO,EAAE,GAAG,KAAmB,CAAC;QACxC,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,OAAO,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type HostContext } from '../host-api.js';
|
|
2
|
+
export declare function canCreateSession(ctx: HostContext): boolean;
|
|
3
|
+
export declare function canArchiveSession(ctx: HostContext): boolean;
|
|
4
|
+
export declare function canUnarchiveSession(ctx: HostContext): boolean;
|
|
5
|
+
export type CreateResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
code: 'no-service' | 'no-folder' | 'failed';
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function createSession(ctx: HostContext, request: {
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
}): Promise<CreateResult>;
|
|
16
|
+
export type UnarchiveResult = {
|
|
17
|
+
ok: true;
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
code: 'no-service' | 'not-found' | 'failed';
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
export declare function unarchiveSession(ctx: HostContext, request: {
|
|
24
|
+
sessionId: string;
|
|
25
|
+
}): Promise<UnarchiveResult>;
|
|
26
|
+
export type ArchiveResult = {
|
|
27
|
+
ok: true;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
code: 'no-service' | 'active' | 'not-found' | 'failed';
|
|
31
|
+
message: string;
|
|
32
|
+
};
|
|
33
|
+
export declare function archiveSession(ctx: HostContext, request: {
|
|
34
|
+
sessionId: string;
|
|
35
|
+
stopActivity?: boolean;
|
|
36
|
+
}): Promise<ArchiveResult>;
|
|
37
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/dsh/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAa3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAO1D;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAO3D;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAO7D;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEhF,wBAAsB,aAAa,CACjC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC/B,OAAO,CAAC,YAAY,CAAC,CAoBvB;AAED,MAAM,MAAM,eAAe,GACzB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7B,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3F,wBAAsB,cAAc,CAClC,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,OAAO,CAAC,aAAa,CAAC,CA0BxB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { service } from '../host-api.js';
|
|
2
|
+
import { codeOf, messageOf } from './errors.js';
|
|
3
|
+
export function canCreateSession(ctx) {
|
|
4
|
+
try {
|
|
5
|
+
const controller = service(ctx, 'sessionController');
|
|
6
|
+
return typeof controller?.create === 'function';
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function canArchiveSession(ctx) {
|
|
13
|
+
try {
|
|
14
|
+
const controller = service(ctx, 'workspaceController');
|
|
15
|
+
return typeof controller?.archiveSession === 'function';
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function canUnarchiveSession(ctx) {
|
|
22
|
+
try {
|
|
23
|
+
const controller = service(ctx, 'workspaceController');
|
|
24
|
+
return typeof controller?.unarchiveSession === 'function';
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export async function createSession(ctx, request) {
|
|
31
|
+
const controller = service(ctx, 'sessionController');
|
|
32
|
+
if (!controller || typeof controller.create !== 'function') {
|
|
33
|
+
return { ok: false, code: 'no-service', message: 'This host cannot create sessions.' };
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const created = await controller.create({ workspaceId: request.workspaceId });
|
|
37
|
+
const sessionId = created?.sessionId;
|
|
38
|
+
if (typeof sessionId !== 'string' || !sessionId) {
|
|
39
|
+
return { ok: false, code: 'failed', message: 'The host did not return a session.' };
|
|
40
|
+
}
|
|
41
|
+
return { ok: true, sessionId };
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const code = codeOf(error);
|
|
45
|
+
if (code === 'workspace/not-found' || code === 'session/not-found') {
|
|
46
|
+
return { ok: false, code: 'no-folder', message: 'That folder no longer exists.' };
|
|
47
|
+
}
|
|
48
|
+
return { ok: false, code: 'failed', message: messageOf(error) };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function unarchiveSession(ctx, request) {
|
|
52
|
+
const controller = service(ctx, 'workspaceController');
|
|
53
|
+
if (!controller || typeof controller.unarchiveSession !== 'function') {
|
|
54
|
+
return { ok: false, code: 'no-service', message: 'This host cannot unarchive sessions.' };
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
await controller.unarchiveSession({ sessionId: request.sessionId });
|
|
58
|
+
return { ok: true };
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const code = codeOf(error);
|
|
62
|
+
if (code === 'session/not-found' || code === 'workspace/not-found') {
|
|
63
|
+
return { ok: false, code: 'not-found', message: 'That session no longer exists.' };
|
|
64
|
+
}
|
|
65
|
+
return { ok: false, code: 'failed', message: messageOf(error) };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export async function archiveSession(ctx, request) {
|
|
69
|
+
const controller = service(ctx, 'workspaceController');
|
|
70
|
+
if (!controller || typeof controller.archiveSession !== 'function') {
|
|
71
|
+
return { ok: false, code: 'no-service', message: 'This host cannot archive sessions.' };
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await controller.archiveSession({
|
|
75
|
+
sessionId: request.sessionId,
|
|
76
|
+
...(request.stopActivity === true ? { stopActivity: true } : {}),
|
|
77
|
+
});
|
|
78
|
+
return { ok: true };
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const code = codeOf(error);
|
|
82
|
+
if (code === 'workspace/session-active') {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
code: 'active',
|
|
86
|
+
message: 'That session has work running. Stop it first.',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (code === 'session/not-found' || code === 'workspace/not-found') {
|
|
90
|
+
return { ok: false, code: 'not-found', message: 'That session no longer exists.' };
|
|
91
|
+
}
|
|
92
|
+
return { ok: false, code: 'failed', message: messageOf(error) };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/dsh/lifecycle.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type HostContext } from '../host-api.js';
|
|
2
|
+
import type { ModelCatalog, ModelSelection } from '@deepseekharness-mobile/protocol';
|
|
3
|
+
interface CatalogPayload {
|
|
4
|
+
default?: unknown;
|
|
5
|
+
groups?: unknown;
|
|
6
|
+
failures?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface ModelController {
|
|
9
|
+
modelCatalog?: () => Promise<CatalogPayload>;
|
|
10
|
+
selectModel?: (request: {
|
|
11
|
+
sessionId: string;
|
|
12
|
+
provider: string;
|
|
13
|
+
model: string;
|
|
14
|
+
reasoningEffort?: string;
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
selected?: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare function selectionOf(value: unknown): ModelSelection | undefined;
|
|
20
|
+
export declare function toCatalog(payload: unknown): ModelCatalog;
|
|
21
|
+
export declare function canListModels(ctx: HostContext): boolean;
|
|
22
|
+
export declare function canSelectModel(ctx: HostContext): boolean;
|
|
23
|
+
export declare function listModels(ctx: HostContext): Promise<ModelCatalog>;
|
|
24
|
+
export type SelectResult = {
|
|
25
|
+
ok: true;
|
|
26
|
+
selection: ModelSelection;
|
|
27
|
+
} | {
|
|
28
|
+
ok: false;
|
|
29
|
+
code: 'no-service' | 'unavailable' | 'not-found' | 'failed';
|
|
30
|
+
message: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function selectModel(ctx: HostContext, request: {
|
|
33
|
+
sessionId: string;
|
|
34
|
+
provider: string;
|
|
35
|
+
model: string;
|
|
36
|
+
reasoningEffort?: string;
|
|
37
|
+
}): Promise<SelectResult>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/dsh/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EACV,YAAY,EAIZ,cAAc,EACf,MAAM,kCAAkC,CAAC;AAgB1C,UAAU,cAAc;IACtB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,cAAc,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE;QACtB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,KAAK,OAAO,CAAC;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACvC;AAMD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS,CActE;AA6ED,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAmBxD;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAMvD;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAMxD;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAcxE;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GACvC;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC5D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEN,wBAAsB,WAAW,CAC/B,GAAG,EAAE,WAAW,EAChB,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACxF,OAAO,CAAC,YAAY,CAAC,CA8BvB"}
|