@skrr-ai/auth-core 0.1.2
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/README.md +21 -0
- package/dist/cjs/aead.d.ts +24 -0
- package/dist/cjs/aead.js +65 -0
- package/dist/cjs/authExpiredDetector.d.ts +65 -0
- package/dist/cjs/authExpiredDetector.js +89 -0
- package/dist/cjs/authHelper.d.ts +51 -0
- package/dist/cjs/authHelper.js +321 -0
- package/dist/cjs/credentialEnvelope.d.ts +85 -0
- package/dist/cjs/credentialEnvelope.js +230 -0
- package/dist/cjs/credentialEnvelopeBridge.d.ts +116 -0
- package/dist/cjs/credentialEnvelopeBridge.js +637 -0
- package/dist/cjs/credentialResolver.d.ts +278 -0
- package/dist/cjs/credentialResolver.js +423 -0
- package/dist/cjs/daemonPair.d.ts +172 -0
- package/dist/cjs/daemonPair.js +495 -0
- package/dist/cjs/daemonScopes.d.ts +74 -0
- package/dist/cjs/daemonScopes.js +84 -0
- package/dist/cjs/deviceIdentityBridge.d.ts +113 -0
- package/dist/cjs/deviceIdentityBridge.js +591 -0
- package/dist/cjs/deviceKey.d.ts +167 -0
- package/dist/cjs/deviceKey.js +332 -0
- package/dist/cjs/fdToken.d.ts +17 -0
- package/dist/cjs/fdToken.js +238 -0
- package/dist/cjs/harnessTrust.d.ts +72 -0
- package/dist/cjs/harnessTrust.js +103 -0
- package/dist/cjs/headers-helper.d.ts +25 -0
- package/dist/cjs/headers-helper.js +323 -0
- package/dist/cjs/index.d.ts +37 -0
- package/dist/cjs/index.js +305 -0
- package/dist/cjs/jwtUtils.d.ts +35 -0
- package/dist/cjs/jwtUtils.js +61 -0
- package/dist/cjs/kek/index.d.ts +89 -0
- package/dist/cjs/kek/index.js +208 -0
- package/dist/cjs/kek/linux.d.ts +92 -0
- package/dist/cjs/kek/linux.js +596 -0
- package/dist/cjs/kek/macos.d.ts +112 -0
- package/dist/cjs/kek/macos.js +599 -0
- package/dist/cjs/kek/types.d.ts +111 -0
- package/dist/cjs/kek/types.js +43 -0
- package/dist/cjs/kek/windows.d.ts +71 -0
- package/dist/cjs/kek/windows.js +350 -0
- package/dist/cjs/kek/zeroize-registry.d.ts +44 -0
- package/dist/cjs/kek/zeroize-registry.js +63 -0
- package/dist/cjs/legacyStatePreflight.d.ts +67 -0
- package/dist/cjs/legacyStatePreflight.js +78 -0
- package/dist/cjs/localIdentity.d.ts +54 -0
- package/dist/cjs/localIdentity.js +57 -0
- package/dist/cjs/loginLocalhost.d.ts +115 -0
- package/dist/cjs/loginLocalhost.js +368 -0
- package/dist/cjs/loginWithLocalhost.d.ts +131 -0
- package/dist/cjs/loginWithLocalhost.js +359 -0
- package/dist/cjs/machineId.d.ts +35 -0
- package/dist/cjs/machineId.js +239 -0
- package/dist/cjs/messages.d.ts +9 -0
- package/dist/cjs/messages.js +44 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/pkce.d.ts +62 -0
- package/dist/cjs/pkce.js +158 -0
- package/dist/cjs/recoveryCode.d.ts +61 -0
- package/dist/cjs/recoveryCode.js +213 -0
- package/dist/cjs/refresh.d.ts +215 -0
- package/dist/cjs/refresh.js +877 -0
- package/dist/cjs/refreshClassification.d.ts +53 -0
- package/dist/cjs/refreshClassification.js +105 -0
- package/dist/cjs/refreshScheduler.d.ts +53 -0
- package/dist/cjs/refreshScheduler.js +332 -0
- package/dist/cjs/runtime.d.ts +149 -0
- package/dist/cjs/runtime.js +136 -0
- package/dist/cjs/spawnEnv.d.ts +76 -0
- package/dist/cjs/spawnEnv.js +153 -0
- package/dist/cjs/ttlParser.d.ts +54 -0
- package/dist/cjs/ttlParser.js +87 -0
- package/dist/cjs/types.d.ts +91 -0
- package/dist/cjs/types.js +26 -0
- package/dist/esm/aead.d.ts +24 -0
- package/dist/esm/aead.js +57 -0
- package/dist/esm/authExpiredDetector.d.ts +65 -0
- package/dist/esm/authExpiredDetector.js +85 -0
- package/dist/esm/authHelper.d.ts +51 -0
- package/dist/esm/authHelper.js +311 -0
- package/dist/esm/credentialEnvelope.d.ts +85 -0
- package/dist/esm/credentialEnvelope.js +213 -0
- package/dist/esm/credentialEnvelopeBridge.d.ts +116 -0
- package/dist/esm/credentialEnvelopeBridge.js +620 -0
- package/dist/esm/credentialResolver.d.ts +278 -0
- package/dist/esm/credentialResolver.js +414 -0
- package/dist/esm/daemonPair.d.ts +172 -0
- package/dist/esm/daemonPair.js +476 -0
- package/dist/esm/daemonScopes.d.ts +74 -0
- package/dist/esm/daemonScopes.js +80 -0
- package/dist/esm/deviceIdentityBridge.d.ts +113 -0
- package/dist/esm/deviceIdentityBridge.js +572 -0
- package/dist/esm/deviceKey.d.ts +167 -0
- package/dist/esm/deviceKey.js +320 -0
- package/dist/esm/fdToken.d.ts +17 -0
- package/dist/esm/fdToken.js +231 -0
- package/dist/esm/harnessTrust.d.ts +72 -0
- package/dist/esm/harnessTrust.js +97 -0
- package/dist/esm/headers-helper.d.ts +25 -0
- package/dist/esm/headers-helper.js +316 -0
- package/dist/esm/index.d.ts +37 -0
- package/dist/esm/index.js +144 -0
- package/dist/esm/jwtUtils.d.ts +35 -0
- package/dist/esm/jwtUtils.js +57 -0
- package/dist/esm/kek/index.d.ts +89 -0
- package/dist/esm/kek/index.js +162 -0
- package/dist/esm/kek/linux.d.ts +92 -0
- package/dist/esm/kek/linux.js +585 -0
- package/dist/esm/kek/macos.d.ts +112 -0
- package/dist/esm/kek/macos.js +586 -0
- package/dist/esm/kek/types.d.ts +111 -0
- package/dist/esm/kek/types.js +39 -0
- package/dist/esm/kek/windows.d.ts +71 -0
- package/dist/esm/kek/windows.js +344 -0
- package/dist/esm/kek/zeroize-registry.d.ts +44 -0
- package/dist/esm/kek/zeroize-registry.js +58 -0
- package/dist/esm/legacyStatePreflight.d.ts +67 -0
- package/dist/esm/legacyStatePreflight.js +71 -0
- package/dist/esm/localIdentity.d.ts +54 -0
- package/dist/esm/localIdentity.js +54 -0
- package/dist/esm/loginLocalhost.d.ts +115 -0
- package/dist/esm/loginLocalhost.js +355 -0
- package/dist/esm/loginWithLocalhost.d.ts +131 -0
- package/dist/esm/loginWithLocalhost.js +353 -0
- package/dist/esm/machineId.d.ts +35 -0
- package/dist/esm/machineId.js +231 -0
- package/dist/esm/messages.d.ts +9 -0
- package/dist/esm/messages.js +40 -0
- package/dist/esm/pkce.d.ts +62 -0
- package/dist/esm/pkce.js +148 -0
- package/dist/esm/recoveryCode.d.ts +61 -0
- package/dist/esm/recoveryCode.js +207 -0
- package/dist/esm/refresh.d.ts +215 -0
- package/dist/esm/refresh.js +863 -0
- package/dist/esm/refreshClassification.d.ts +53 -0
- package/dist/esm/refreshClassification.js +106 -0
- package/dist/esm/refreshScheduler.d.ts +53 -0
- package/dist/esm/refreshScheduler.js +329 -0
- package/dist/esm/runtime.d.ts +149 -0
- package/dist/esm/runtime.js +121 -0
- package/dist/esm/spawnEnv.d.ts +76 -0
- package/dist/esm/spawnEnv.js +149 -0
- package/dist/esm/ttlParser.d.ts +54 -0
- package/dist/esm/ttlParser.js +83 -0
- package/dist/esm/types.d.ts +91 -0
- package/dist/esm/types.js +21 -0
- package/package.json +82 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect state left by the pre-rename product on this machine.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* ---------------
|
|
6
|
+
* §5.5 of the rename scope doc states the invariant plainly: the same machine
|
|
7
|
+
* must never have an old and a new runtime live at once. §10 names the failure
|
|
8
|
+
* it prevents — "dual daemon race", two binaries under two different service
|
|
9
|
+
* labels, each believing it holds the single-instance lock because neither can
|
|
10
|
+
* see the other's label.
|
|
11
|
+
*
|
|
12
|
+
* A rename cannot prevent that on its own. Changing `~/.oversky` to `~/.skrr`
|
|
13
|
+
* does not remove the old directory or unregister the old service; it just
|
|
14
|
+
* stops looking at them. So the new runtime has to LOOK, and refuse.
|
|
15
|
+
*
|
|
16
|
+
* DETECT AND REFUSE, NEVER MIGRATE AND NEVER DELETE
|
|
17
|
+
* -------------------------------------------------
|
|
18
|
+
* §0.1 was confirmed on 2026-08-31: there are no external users, and the team
|
|
19
|
+
* accepts re-login and reinstall. §3.2 therefore makes RESET the documented
|
|
20
|
+
* default, so this deliberately does not read, copy or repair old state:
|
|
21
|
+
*
|
|
22
|
+
* - Copying it forward would contradict the clean break, and §7.2 forbids
|
|
23
|
+
* copying a refresh token into a new location at all.
|
|
24
|
+
* - Deleting it would be worse. §8.4: never delete local state, Keychain
|
|
25
|
+
* entries or feeds to force a migration. The operator decides, from the
|
|
26
|
+
* cutover runbook, after taking the backup that runbook specifies.
|
|
27
|
+
*
|
|
28
|
+
* The only correct behaviour is to say precisely what was found and stop.
|
|
29
|
+
*
|
|
30
|
+
* BEST EFFORT BY CONSTRUCTION
|
|
31
|
+
* ---------------------------
|
|
32
|
+
* An unreadable home directory, a permissions error or an exotic platform must
|
|
33
|
+
* never be the reason an install cannot proceed — a preflight that blocks a
|
|
34
|
+
* clean machine is worse than one that misses a dirty one. Every probe is
|
|
35
|
+
* wrapped, and anything it cannot determine reads as "not found".
|
|
36
|
+
*/
|
|
37
|
+
export interface LegacyStateFinding {
|
|
38
|
+
/** Absolute path that still exists. */
|
|
39
|
+
readonly path: string;
|
|
40
|
+
/** The directory name that identified it, e.g. `.oversky`. */
|
|
41
|
+
readonly root: string;
|
|
42
|
+
/** True when the directory holds a profile tree, i.e. a runtime actually ran. */
|
|
43
|
+
readonly hasProfiles: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface LegacyStateReport {
|
|
46
|
+
readonly findings: readonly LegacyStateFinding[];
|
|
47
|
+
/** True when anything was found. Callers refuse on this. */
|
|
48
|
+
readonly hasLegacyState: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Look for pre-rename state roots under `home`.
|
|
52
|
+
*
|
|
53
|
+
* `home` is injectable so the test suite can point it at a fixture rather than
|
|
54
|
+
* at the developer's actual home directory — a preflight test that reads the
|
|
55
|
+
* real `~` passes or fails based on who runs it.
|
|
56
|
+
*/
|
|
57
|
+
export declare function findLegacyLocalState(home?: string): LegacyStateReport;
|
|
58
|
+
/**
|
|
59
|
+
* The message shown when preflight refuses.
|
|
60
|
+
*
|
|
61
|
+
* Written for someone who has just been stopped and wants to know why and what
|
|
62
|
+
* to do — so it names the exact paths, says explicitly that nothing was touched,
|
|
63
|
+
* and points at the runbook rather than improvising a fix. It does NOT offer a
|
|
64
|
+
* `--force`: the operator's next step is the backup the runbook specifies, and
|
|
65
|
+
* a flag that skips a safety check is the flag everyone learns to paste.
|
|
66
|
+
*/
|
|
67
|
+
export declare function describeLegacyState(report: LegacyStateReport, binaryName: string): string;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { LEGACY_CONFIG_DIR_NAMES } from './localIdentity.js';
|
|
5
|
+
function safeExists(p) {
|
|
6
|
+
try {
|
|
7
|
+
return fs.existsSync(p);
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function safeHasProfiles(dir) {
|
|
14
|
+
try {
|
|
15
|
+
const profiles = path.join(dir, 'profiles');
|
|
16
|
+
return fs.existsSync(profiles) && fs.readdirSync(profiles).length > 0;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Look for pre-rename state roots under `home`.
|
|
24
|
+
*
|
|
25
|
+
* `home` is injectable so the test suite can point it at a fixture rather than
|
|
26
|
+
* at the developer's actual home directory — a preflight test that reads the
|
|
27
|
+
* real `~` passes or fails based on who runs it.
|
|
28
|
+
*/
|
|
29
|
+
export function findLegacyLocalState(home = os.homedir()) {
|
|
30
|
+
const findings = [];
|
|
31
|
+
// No guard against a legacy name equalling the CURRENT root: `CONFIG_DIR_NAME`
|
|
32
|
+
// and `LEGACY_CONFIG_DIR_NAMES` are both literal-typed, so TypeScript proves
|
|
33
|
+
// they cannot overlap and rejects the comparison as unreachable. A runtime
|
|
34
|
+
// check here would be dead code pretending to be a safety net. The
|
|
35
|
+
// disjointness is asserted in the test instead, where it can catch a future
|
|
36
|
+
// edit to the constants.
|
|
37
|
+
for (const root of LEGACY_CONFIG_DIR_NAMES) {
|
|
38
|
+
const dir = path.join(home, root);
|
|
39
|
+
if (!safeExists(dir))
|
|
40
|
+
continue;
|
|
41
|
+
findings.push({ path: dir, root, hasProfiles: safeHasProfiles(dir) });
|
|
42
|
+
}
|
|
43
|
+
return { findings, hasLegacyState: findings.length > 0 };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The message shown when preflight refuses.
|
|
47
|
+
*
|
|
48
|
+
* Written for someone who has just been stopped and wants to know why and what
|
|
49
|
+
* to do — so it names the exact paths, says explicitly that nothing was touched,
|
|
50
|
+
* and points at the runbook rather than improvising a fix. It does NOT offer a
|
|
51
|
+
* `--force`: the operator's next step is the backup the runbook specifies, and
|
|
52
|
+
* a flag that skips a safety check is the flag everyone learns to paste.
|
|
53
|
+
*/
|
|
54
|
+
export function describeLegacyState(report, binaryName) {
|
|
55
|
+
const lines = [];
|
|
56
|
+
lines.push('Found state from the previous product identity on this machine:');
|
|
57
|
+
lines.push('');
|
|
58
|
+
for (const f of report.findings) {
|
|
59
|
+
lines.push(` ${f.path}${f.hasProfiles ? ' (a runtime has run from here)' : ''}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push('');
|
|
62
|
+
lines.push('Installing beside it risks two runtimes holding two different service');
|
|
63
|
+
lines.push('labels, each believing it owns the single-instance lock.');
|
|
64
|
+
lines.push('');
|
|
65
|
+
lines.push('Nothing above has been read, copied or removed. Follow the team cutover');
|
|
66
|
+
lines.push('runbook — it takes a backup first — and then run this again:');
|
|
67
|
+
lines.push('');
|
|
68
|
+
lines.push(` ${binaryName} uninstall # stop and deregister the old service`);
|
|
69
|
+
lines.push(' # then remove the directories listed above, per the runbook');
|
|
70
|
+
return lines.join('\n');
|
|
71
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names this product uses for state it leaves on a machine.
|
|
3
|
+
*
|
|
4
|
+
* WHY THESE LIVE IN ONE PLACE NOW
|
|
5
|
+
* ------------------------------
|
|
6
|
+
* Before the OverSky → skrr rename, `'.oversky'` was written as a literal in 47
|
|
7
|
+
* places across `auth-core`, `cli`, `daemon` and `desktop`, and the Keychain
|
|
8
|
+
* service prefix in about as many. Nothing related them, so nothing could tell
|
|
9
|
+
* you whether a given path was the machine root, a profile directory, or a
|
|
10
|
+
* coincidence — and changing the value meant finding all 47 and hoping.
|
|
11
|
+
*
|
|
12
|
+
* `auth-core` is the right home because it is the one module all three runtimes
|
|
13
|
+
* already import: the CLI (42 files), the daemon (35) and Desktop (5).
|
|
14
|
+
*
|
|
15
|
+
* WHAT MAKES THESE DIFFERENT FROM COPY
|
|
16
|
+
* ------------------------------------
|
|
17
|
+
* A name here ADDRESSES state that already exists on a real machine. Changing
|
|
18
|
+
* `CONFIG_DIR_NAME` does not move `~/.oversky` to `~/.skrr` — it makes the old
|
|
19
|
+
* directory unreachable and starts a new, empty one. The same is true of the
|
|
20
|
+
* Keychain service: an item is addressed BY service name, so a rename orphans
|
|
21
|
+
* the wrapped credential rather than migrating it.
|
|
22
|
+
*
|
|
23
|
+
* That is the intended behaviour under the pre-launch clean break (§0.1
|
|
24
|
+
* confirmed 2026-08-31: no external users, and the team accepts re-login). It is
|
|
25
|
+
* only safe because of the LEGACY_ values below, which exist so install
|
|
26
|
+
* preflight can NAME the old state in order to detect and refuse it. Preflight
|
|
27
|
+
* cannot refuse what it cannot name.
|
|
28
|
+
*/
|
|
29
|
+
/** Machine-scoped state root, relative to the user's home directory. */
|
|
30
|
+
export declare const CONFIG_DIR_NAME = ".skrr";
|
|
31
|
+
/**
|
|
32
|
+
* Roots this product used before, newest first.
|
|
33
|
+
*
|
|
34
|
+
* NOT a compatibility path — nothing reads state from these. They exist so
|
|
35
|
+
* install preflight and the team cutover runbook can detect a machine that
|
|
36
|
+
* still carries old state and refuse to run beside it (§5.5: the same machine
|
|
37
|
+
* must never have an old and a new runtime live at once).
|
|
38
|
+
*
|
|
39
|
+
* `.sky` predates `.oversky` and was already handled as a legacy path before
|
|
40
|
+
* this rename, so it is carried forward rather than forgotten.
|
|
41
|
+
*/
|
|
42
|
+
export declare const LEGACY_CONFIG_DIR_NAMES: readonly [".oversky", ".sky"];
|
|
43
|
+
/** Prefix for OS service labels and Keychain services (`ai.skrr.daemon`, …). */
|
|
44
|
+
export declare const NATIVE_ID_PREFIX = "ai.skrr";
|
|
45
|
+
/**
|
|
46
|
+
* The previous native prefix, for the same detect-and-refuse reason.
|
|
47
|
+
*
|
|
48
|
+
* Note the KEK identifier used `com.oversky.daemon.kek` while everything else
|
|
49
|
+
* used `ai.oversky.*`. Both are listed, because preflight has to recognise a
|
|
50
|
+
* machine carrying either.
|
|
51
|
+
*/
|
|
52
|
+
export declare const LEGACY_NATIVE_ID_PREFIXES: readonly ["ai.oversky", "com.oversky"];
|
|
53
|
+
/** The user-facing binary name used in reauth messages. Overridden per runtime. */
|
|
54
|
+
export declare const DEFAULT_BINARY_NAME = "skrrd";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The names this product uses for state it leaves on a machine.
|
|
3
|
+
*
|
|
4
|
+
* WHY THESE LIVE IN ONE PLACE NOW
|
|
5
|
+
* ------------------------------
|
|
6
|
+
* Before the OverSky → skrr rename, `'.oversky'` was written as a literal in 47
|
|
7
|
+
* places across `auth-core`, `cli`, `daemon` and `desktop`, and the Keychain
|
|
8
|
+
* service prefix in about as many. Nothing related them, so nothing could tell
|
|
9
|
+
* you whether a given path was the machine root, a profile directory, or a
|
|
10
|
+
* coincidence — and changing the value meant finding all 47 and hoping.
|
|
11
|
+
*
|
|
12
|
+
* `auth-core` is the right home because it is the one module all three runtimes
|
|
13
|
+
* already import: the CLI (42 files), the daemon (35) and Desktop (5).
|
|
14
|
+
*
|
|
15
|
+
* WHAT MAKES THESE DIFFERENT FROM COPY
|
|
16
|
+
* ------------------------------------
|
|
17
|
+
* A name here ADDRESSES state that already exists on a real machine. Changing
|
|
18
|
+
* `CONFIG_DIR_NAME` does not move `~/.oversky` to `~/.skrr` — it makes the old
|
|
19
|
+
* directory unreachable and starts a new, empty one. The same is true of the
|
|
20
|
+
* Keychain service: an item is addressed BY service name, so a rename orphans
|
|
21
|
+
* the wrapped credential rather than migrating it.
|
|
22
|
+
*
|
|
23
|
+
* That is the intended behaviour under the pre-launch clean break (§0.1
|
|
24
|
+
* confirmed 2026-08-31: no external users, and the team accepts re-login). It is
|
|
25
|
+
* only safe because of the LEGACY_ values below, which exist so install
|
|
26
|
+
* preflight can NAME the old state in order to detect and refuse it. Preflight
|
|
27
|
+
* cannot refuse what it cannot name.
|
|
28
|
+
*/
|
|
29
|
+
/** Machine-scoped state root, relative to the user's home directory. */
|
|
30
|
+
export const CONFIG_DIR_NAME = '.skrr';
|
|
31
|
+
/**
|
|
32
|
+
* Roots this product used before, newest first.
|
|
33
|
+
*
|
|
34
|
+
* NOT a compatibility path — nothing reads state from these. They exist so
|
|
35
|
+
* install preflight and the team cutover runbook can detect a machine that
|
|
36
|
+
* still carries old state and refuse to run beside it (§5.5: the same machine
|
|
37
|
+
* must never have an old and a new runtime live at once).
|
|
38
|
+
*
|
|
39
|
+
* `.sky` predates `.oversky` and was already handled as a legacy path before
|
|
40
|
+
* this rename, so it is carried forward rather than forgotten.
|
|
41
|
+
*/
|
|
42
|
+
export const LEGACY_CONFIG_DIR_NAMES = ['.oversky', '.sky'];
|
|
43
|
+
/** Prefix for OS service labels and Keychain services (`ai.skrr.daemon`, …). */
|
|
44
|
+
export const NATIVE_ID_PREFIX = 'ai.skrr';
|
|
45
|
+
/**
|
|
46
|
+
* The previous native prefix, for the same detect-and-refuse reason.
|
|
47
|
+
*
|
|
48
|
+
* Note the KEK identifier used `com.oversky.daemon.kek` while everything else
|
|
49
|
+
* used `ai.oversky.*`. Both are listed, because preflight has to recognise a
|
|
50
|
+
* machine carrying either.
|
|
51
|
+
*/
|
|
52
|
+
export const LEGACY_NATIVE_ID_PREFIXES = ['ai.oversky', 'com.oversky'];
|
|
53
|
+
/** The user-facing binary name used in reauth messages. Overridden per runtime. */
|
|
54
|
+
export const DEFAULT_BINARY_NAME = 'skrrd';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/** Default total wait (ms) for the callback. Matches consent-page UX. */
|
|
2
|
+
export declare const DEFAULT_LOGIN_TIMEOUT_MS: number;
|
|
3
|
+
export interface StartLocalCallbackOptions {
|
|
4
|
+
/**
|
|
5
|
+
* PKCE state parameter — must match the value sent to the consent
|
|
6
|
+
* page in `/api/auth/cli-init`. Generate via `pkce.generateState()`.
|
|
7
|
+
*/
|
|
8
|
+
state: string;
|
|
9
|
+
/**
|
|
10
|
+
* Port to bind. 0 (default) requests an ephemeral kernel-assigned
|
|
11
|
+
* port. Pin a port (via `--port` or `OVERSKY_LOGIN_PORT`) when the
|
|
12
|
+
* user is in a firewall-allowlisted environment that pre-opened a
|
|
13
|
+
* single localhost port for OAuth flows.
|
|
14
|
+
*/
|
|
15
|
+
port?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Total time the server waits for a callback before timing out.
|
|
18
|
+
* Default 2 minutes. After this elapses the server closes and the
|
|
19
|
+
* callback promise rejects with `LocalCallbackTimeoutError`.
|
|
20
|
+
*/
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Multica-convergence (P1 #5) — override the host that appears in
|
|
24
|
+
* `redirectUri` AND the listen address. Defaults to `'127.0.0.1'`
|
|
25
|
+
* and SHOULD stay that way for every local-browser flow.
|
|
26
|
+
*
|
|
27
|
+
* The override exists for one legitimate case: the CLI is on a
|
|
28
|
+
* different machine than the browser (e.g. a LAN-only dev box a
|
|
29
|
+
* teammate's browser needs to reach). In that scenario the user
|
|
30
|
+
* passes `--callback-host <lan-ip>` and the server binds on that
|
|
31
|
+
* interface so the browser can reach it.
|
|
32
|
+
*
|
|
33
|
+
* Binding a non-loopback host widens the blast radius — any device
|
|
34
|
+
* on that network segment can race the browser to the callback.
|
|
35
|
+
* Callers SHOULD only enable this after confirming with the user
|
|
36
|
+
* that the LAN is trusted. A warning is emitted on non-loopback
|
|
37
|
+
* binds.
|
|
38
|
+
*
|
|
39
|
+
* SSH port-forward users DO NOT need this — forwarding
|
|
40
|
+
* `-L 1455:127.0.0.1:1455` makes the remote's 127.0.0.1 reachable
|
|
41
|
+
* from the local browser without touching `host`.
|
|
42
|
+
*/
|
|
43
|
+
host?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Optional split between bind address and URL host. When provided,
|
|
46
|
+
* the server listens on `bindAddr` and the `redirectUri` is built
|
|
47
|
+
* from `host`. Use sparingly — e.g. `bindAddr='0.0.0.0'` with
|
|
48
|
+
* `host='10.0.0.5'` lets a browser on a teammate's machine reach
|
|
49
|
+
* a CLI on the LAN without hardcoding the LAN IP into the bind.
|
|
50
|
+
*
|
|
51
|
+
* Defaults to `host` when omitted.
|
|
52
|
+
*/
|
|
53
|
+
bindAddr?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface LocalCallbackResult {
|
|
56
|
+
/** Authorization code returned by the server (single-use). */
|
|
57
|
+
code: string;
|
|
58
|
+
/** State echoed back, post-validation. */
|
|
59
|
+
state: string;
|
|
60
|
+
/**
|
|
61
|
+
* The redirect URI this server bound to. Caller MUST pass this same
|
|
62
|
+
* string to /cli-init AND /cli-token-exchange (RFC 6749 §4.1.3
|
|
63
|
+
* redirect_uri-binding requirement).
|
|
64
|
+
*/
|
|
65
|
+
redirectUri: string;
|
|
66
|
+
}
|
|
67
|
+
export interface LocalCallbackHandle {
|
|
68
|
+
/**
|
|
69
|
+
* Bound redirect URI in the form `http://127.0.0.1:<port>/callback`.
|
|
70
|
+
* Available synchronously after `startLocalCallback()` resolves.
|
|
71
|
+
*/
|
|
72
|
+
redirectUri: string;
|
|
73
|
+
/**
|
|
74
|
+
* Resolves with the callback result on the first valid callback.
|
|
75
|
+
* Rejects on timeout, denial, state mismatch, malformed callback,
|
|
76
|
+
* post-bind server error, or caller cancellation. Safe to call
|
|
77
|
+
* multiple times — returns the same promise.
|
|
78
|
+
*/
|
|
79
|
+
waitForCallback(): Promise<LocalCallbackResult>;
|
|
80
|
+
/** Cancel the wait and tear down the server. Idempotent. */
|
|
81
|
+
close(): void;
|
|
82
|
+
}
|
|
83
|
+
export declare class LocalCallbackError extends Error {
|
|
84
|
+
readonly code: string;
|
|
85
|
+
constructor(code: string, message: string);
|
|
86
|
+
}
|
|
87
|
+
export declare class LocalCallbackBindError extends LocalCallbackError {
|
|
88
|
+
readonly port: number;
|
|
89
|
+
readonly cause?: Error;
|
|
90
|
+
constructor(port: number, cause?: Error);
|
|
91
|
+
}
|
|
92
|
+
export declare class LocalCallbackTimeoutError extends LocalCallbackError {
|
|
93
|
+
readonly timeoutMs: number;
|
|
94
|
+
constructor(timeoutMs: number);
|
|
95
|
+
}
|
|
96
|
+
export declare class LocalCallbackStateMismatchError extends LocalCallbackError {
|
|
97
|
+
constructor();
|
|
98
|
+
}
|
|
99
|
+
export declare class LocalCallbackDeniedError extends LocalCallbackError {
|
|
100
|
+
readonly serverError: string;
|
|
101
|
+
constructor(serverError: string, description?: string);
|
|
102
|
+
}
|
|
103
|
+
export declare class LocalCallbackMalformedError extends LocalCallbackError {
|
|
104
|
+
constructor(reason: string);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Start the local 127.0.0.1 callback server. Binds synchronously
|
|
108
|
+
* (await'd via the listen handshake) and returns a handle whose
|
|
109
|
+
* `redirectUri` is immediately usable. The caller drives the rest of
|
|
110
|
+
* the flow and awaits `waitForCallback()` when ready.
|
|
111
|
+
*
|
|
112
|
+
* Throws `LocalCallbackBindError` if the bind fails (caller decides
|
|
113
|
+
* whether to retry on a different port or fall back to device-code).
|
|
114
|
+
*/
|
|
115
|
+
export declare function startLocalCallback(opts: StartLocalCallbackOptions): Promise<LocalCallbackHandle>;
|