@linkshell/host 0.4.60 → 0.4.62
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/dist/host/src/access.d.ts +28 -0
- package/dist/host/src/access.js +106 -0
- package/dist/host/src/access.js.map +1 -0
- package/dist/host/src/drivers/acp/antigravity.js +8 -4
- package/dist/host/src/drivers/acp/antigravity.js.map +1 -1
- package/dist/host/src/drivers/claude/transcript.d.ts +5 -5
- package/dist/host/src/gateway.d.ts +2 -0
- package/dist/host/src/gateway.js +2 -0
- package/dist/host/src/gateway.js.map +1 -1
- package/dist/host/src/host.js +7 -1
- package/dist/host/src/host.js.map +1 -1
- package/dist/host/src/hub.d.ts +4 -4
- package/dist/host/src/index.d.ts +1 -0
- package/dist/host/src/index.js +1 -0
- package/dist/host/src/index.js.map +1 -1
- package/dist/host/src/rpc/server.d.ts +10 -0
- package/dist/host/src/rpc/server.js +74 -17
- package/dist/host/src/rpc/server.js.map +1 -1
- package/dist/host/src/screen-session.d.ts +2 -2
- package/dist/host/tsconfig.tsbuildinfo +1 -1
- package/dist/wire/src/editor.d.ts +12 -12
- package/dist/wire/src/live-activity.d.ts +2 -2
- package/dist/wire/src/model.d.ts +32 -28
- package/dist/wire/src/model.js +2 -0
- package/dist/wire/src/model.js.map +1 -1
- package/dist/wire/src/relay-client.d.ts +2 -0
- package/dist/wire/src/relay-client.js +1 -0
- package/dist/wire/src/relay-client.js.map +1 -1
- package/dist/wire/src/relay.d.ts +14 -6
- package/dist/wire/src/relay.js +4 -0
- package/dist/wire/src/relay.js.map +1 -1
- package/dist/wire/src/rpc.d.ts +371 -313
- package/dist/wire/src/rpc.js +19 -0
- package/dist/wire/src/rpc.js.map +1 -1
- package/dist/wire/src/updates.d.ts +44 -44
- package/dist/wire/src/watch.d.ts +18 -18
- package/package.json +3 -3
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function accessPath(home: string): string;
|
|
2
|
+
/** Writes or removes the password file directly (the CLI, when no host is running). */
|
|
3
|
+
export declare function writeAccessPassword(home: string, password: string | undefined): void;
|
|
4
|
+
export type UnlockResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
token: string;
|
|
7
|
+
expiresAt: number;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
reason: "wrong" | "throttled" | "invalid_token";
|
|
11
|
+
};
|
|
12
|
+
/** The computer's optional access password. Remote connections must unlock before anything else. */
|
|
13
|
+
export declare class AccessControl {
|
|
14
|
+
private readonly home;
|
|
15
|
+
private file;
|
|
16
|
+
private readonly failures;
|
|
17
|
+
constructor(home: string);
|
|
18
|
+
get enabled(): boolean;
|
|
19
|
+
set(password: string | undefined): void;
|
|
20
|
+
unlock(deviceId: string, input: {
|
|
21
|
+
password?: string;
|
|
22
|
+
token?: string;
|
|
23
|
+
}, now?: number): UnlockResult;
|
|
24
|
+
private matches;
|
|
25
|
+
private issue;
|
|
26
|
+
private verifyToken;
|
|
27
|
+
private sign;
|
|
28
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { createHmac, randomBytes, scryptSync, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { chmodSync, existsSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
/** scrypt cost: ~50 ms per guess here, on top of the per-device throttle. */
|
|
5
|
+
const SCRYPT = { N: 16384, r: 8, p: 1, keylen: 32 };
|
|
6
|
+
const TOKEN_TTL_MS = 7 * 24 * 60 * 60_000;
|
|
7
|
+
const FAILURE_WINDOW_MS = 5 * 60_000;
|
|
8
|
+
const MAX_FAILURES = 5;
|
|
9
|
+
export function accessPath(home) {
|
|
10
|
+
return join(home, "access.json");
|
|
11
|
+
}
|
|
12
|
+
/** Writes or removes the password file directly (the CLI, when no host is running). */
|
|
13
|
+
export function writeAccessPassword(home, password) {
|
|
14
|
+
const path = accessPath(home);
|
|
15
|
+
if (!password) {
|
|
16
|
+
rmSync(path, { force: true });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const salt = randomBytes(16);
|
|
20
|
+
const file = {
|
|
21
|
+
salt: salt.toString("base64"),
|
|
22
|
+
hash: scryptSync(password, salt, SCRYPT.keylen, SCRYPT).toString("base64"),
|
|
23
|
+
secret: randomBytes(32).toString("base64"),
|
|
24
|
+
};
|
|
25
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
26
|
+
writeFileSync(temp, `${JSON.stringify(file)}\n`, { mode: 0o600 });
|
|
27
|
+
chmodSync(temp, 0o600);
|
|
28
|
+
renameSync(temp, path);
|
|
29
|
+
}
|
|
30
|
+
function readAccessFile(home) {
|
|
31
|
+
const path = accessPath(home);
|
|
32
|
+
if (!existsSync(path))
|
|
33
|
+
return undefined;
|
|
34
|
+
try {
|
|
35
|
+
const file = JSON.parse(readFileSync(path, "utf8"));
|
|
36
|
+
if (typeof file.salt === "string" && typeof file.hash === "string" && typeof file.secret === "string")
|
|
37
|
+
return file;
|
|
38
|
+
}
|
|
39
|
+
catch { /* handled below */ }
|
|
40
|
+
// A damaged file must not silently open the computer: keep it locked with no password that matches.
|
|
41
|
+
return { salt: "", hash: "", secret: randomBytes(32).toString("base64") };
|
|
42
|
+
}
|
|
43
|
+
/** The computer's optional access password. Remote connections must unlock before anything else. */
|
|
44
|
+
export class AccessControl {
|
|
45
|
+
home;
|
|
46
|
+
file;
|
|
47
|
+
failures = new Map();
|
|
48
|
+
constructor(home) {
|
|
49
|
+
this.home = home;
|
|
50
|
+
this.file = readAccessFile(home);
|
|
51
|
+
}
|
|
52
|
+
get enabled() {
|
|
53
|
+
return this.file !== undefined;
|
|
54
|
+
}
|
|
55
|
+
set(password) {
|
|
56
|
+
writeAccessPassword(this.home, password);
|
|
57
|
+
this.file = readAccessFile(this.home);
|
|
58
|
+
this.failures.clear();
|
|
59
|
+
}
|
|
60
|
+
unlock(deviceId, input, now = Date.now()) {
|
|
61
|
+
const file = this.file;
|
|
62
|
+
if (!file)
|
|
63
|
+
return { ok: true, ...this.issue(deviceId, now) };
|
|
64
|
+
if (input.token !== undefined && input.password === undefined) {
|
|
65
|
+
return this.verifyToken(file, deviceId, input.token, now) ? { ok: true, ...this.issue(deviceId, now) } : { ok: false, reason: "invalid_token" };
|
|
66
|
+
}
|
|
67
|
+
const failure = this.failures.get(deviceId);
|
|
68
|
+
if (failure && now - failure.since < FAILURE_WINDOW_MS && failure.count >= MAX_FAILURES)
|
|
69
|
+
return { ok: false, reason: "throttled" };
|
|
70
|
+
if (this.matches(file, input.password ?? "")) {
|
|
71
|
+
this.failures.delete(deviceId);
|
|
72
|
+
return { ok: true, ...this.issue(deviceId, now) };
|
|
73
|
+
}
|
|
74
|
+
if (!failure || now - failure.since >= FAILURE_WINDOW_MS)
|
|
75
|
+
this.failures.set(deviceId, { since: now, count: 1 });
|
|
76
|
+
else
|
|
77
|
+
failure.count++;
|
|
78
|
+
return { ok: false, reason: "wrong" };
|
|
79
|
+
}
|
|
80
|
+
matches(file, password) {
|
|
81
|
+
if (!file.hash)
|
|
82
|
+
return false;
|
|
83
|
+
const expected = Buffer.from(file.hash, "base64");
|
|
84
|
+
const actual = scryptSync(password, Buffer.from(file.salt, "base64"), expected.length, SCRYPT);
|
|
85
|
+
return timingSafeEqual(actual, expected);
|
|
86
|
+
}
|
|
87
|
+
issue(deviceId, now) {
|
|
88
|
+
const expiresAt = now + TOKEN_TTL_MS;
|
|
89
|
+
if (!this.file)
|
|
90
|
+
return { token: "", expiresAt };
|
|
91
|
+
return { token: `${expiresAt}.${this.sign(this.file, deviceId, expiresAt)}`, expiresAt };
|
|
92
|
+
}
|
|
93
|
+
verifyToken(file, deviceId, token, now) {
|
|
94
|
+
const [expires, signature] = token.split(".");
|
|
95
|
+
const expiresAt = Number(expires);
|
|
96
|
+
if (!signature || !Number.isFinite(expiresAt) || expiresAt <= now)
|
|
97
|
+
return false;
|
|
98
|
+
const expected = Buffer.from(this.sign(file, deviceId, expiresAt));
|
|
99
|
+
const actual = Buffer.from(signature);
|
|
100
|
+
return actual.length === expected.length && timingSafeEqual(actual, expected);
|
|
101
|
+
}
|
|
102
|
+
sign(file, deviceId, expiresAt) {
|
|
103
|
+
return createHmac("sha256", Buffer.from(file.secret, "base64")).update(`linkshell-access:${deviceId}:${expiresAt}`).digest("base64url");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../../src/access.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACjG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,6EAA6E;AAC7E,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAW,CAAC;AAC7D,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;AAC1C,MAAM,iBAAiB,GAAG,CAAC,GAAG,MAAM,CAAC;AACrC,MAAM,YAAY,GAAG,CAAC,CAAC;AASvB,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AACnC,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAA4B;IAC5E,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7B,MAAM,IAAI,GAAe;QACvB,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC7B,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC1E,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAC3C,CAAC;IACF,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAwB,CAAC;QAC3E,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,IAAkB,CAAC;IACnI,CAAC;IAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC/B,oGAAoG;IACpG,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC5E,CAAC;AAID,oGAAoG;AACpG,MAAM,OAAO,aAAa;IAIK;IAHrB,IAAI,CAAyB;IACpB,QAAQ,GAAG,IAAI,GAAG,EAA4C,CAAC;IAEhF,YAA6B,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;QACvC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC;IACjC,CAAC;IAED,GAAG,CAAC,QAA4B;QAC9B,mBAAmB,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,MAAM,CAAC,QAAgB,EAAE,KAA4C,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;QACrF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;QAC7D,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC9D,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;QAClJ,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,OAAO,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,GAAG,iBAAiB,IAAI,OAAO,CAAC,KAAK,IAAI,YAAY;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QACnI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC/B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;QACpD,CAAC;QACD,IAAI,CAAC,OAAO,IAAI,GAAG,GAAG,OAAO,CAAC,KAAK,IAAI,iBAAiB;YAAE,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;;YAC3G,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IACxC,CAAC;IAEO,OAAO,CAAC,IAAgB,EAAE,QAAgB;QAChD,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;QAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/F,OAAO,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAEO,KAAK,CAAC,QAAgB,EAAE,GAAW;QACzC,MAAM,SAAS,GAAG,GAAG,GAAG,YAAY,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;QAChD,OAAO,EAAE,KAAK,EAAE,GAAG,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC;IAC3F,CAAC;IAEO,WAAW,CAAC,IAAgB,EAAE,QAAgB,EAAE,KAAa,EAAE,GAAW;QAChF,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,GAAG;YAAE,OAAO,KAAK,CAAC;QAChF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,IAAI,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAChF,CAAC;IAEO,IAAI,CAAC,IAAgB,EAAE,QAAgB,EAAE,SAAiB;QAChE,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,oBAAoB,QAAQ,IAAI,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAC1I,CAAC;CACF"}
|
|
@@ -348,7 +348,10 @@ export function cleanAntigravityAgentContent(content) {
|
|
|
348
348
|
function extractModelSelectionFromContent(content) {
|
|
349
349
|
if (!content)
|
|
350
350
|
return undefined;
|
|
351
|
-
|
|
351
|
+
// Names carry dots ("Claude Opus 5.5 (High)", "Gemini 3.8 Flash (Low)"): one ends at the sentence's
|
|
352
|
+
// full stop, a dot followed by a space or the line's end. Stopping at the first dot made the match
|
|
353
|
+
// skip ahead to "No need to comment…" and read that as the model.
|
|
354
|
+
const regex = /changed setting `?Model Selection`? from .+? to (.+?)(?:\.(?=\s|$)|<\/USER_SETTINGS_CHANGE>|$)/gim;
|
|
352
355
|
let last;
|
|
353
356
|
let match;
|
|
354
357
|
while ((match = regex.exec(content)) !== null) {
|
|
@@ -1664,9 +1667,7 @@ export class AntigravityDriver extends AcpDriver {
|
|
|
1664
1667
|
if (found)
|
|
1665
1668
|
return found;
|
|
1666
1669
|
}
|
|
1667
|
-
|
|
1668
|
-
if (fromDefault)
|
|
1669
|
-
return fromDefault;
|
|
1670
|
+
// The model the user last picked in the app comes before the server's default.
|
|
1670
1671
|
const desktop = this.desktop();
|
|
1671
1672
|
if (desktop && existsSync(join(desktop.dataDir, "antigravity_state.pbtxt"))) {
|
|
1672
1673
|
try {
|
|
@@ -1680,6 +1681,9 @@ export class AntigravityDriver extends AcpDriver {
|
|
|
1680
1681
|
// ignore
|
|
1681
1682
|
}
|
|
1682
1683
|
}
|
|
1684
|
+
const fromDefault = matchByValueOrName(this.desktopDefaultModel);
|
|
1685
|
+
if (fromDefault)
|
|
1686
|
+
return fromDefault;
|
|
1683
1687
|
return models[0];
|
|
1684
1688
|
}
|
|
1685
1689
|
async resolvePlanModelEnum(conn, nativeId, explicitModel) {
|