@oh-hai/cli 0.1.0-beta.0 → 0.2.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/README.md +51 -8
- package/dist/auth/resolve-token.d.ts +2 -4
- package/dist/auth/resolve-token.js +4 -11
- package/dist/auth/resolve-token.js.map +1 -1
- package/dist/bindings.d.ts +65 -0
- package/dist/bindings.js +242 -0
- package/dist/bindings.js.map +1 -0
- package/dist/cli.js +211 -12
- package/dist/cli.js.map +1 -1
- package/dist/commands/ask.js +21 -17
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/binary-update.d.ts +61 -0
- package/dist/commands/binary-update.js +160 -0
- package/dist/commands/binary-update.js.map +1 -0
- package/dist/commands/context.d.ts +135 -0
- package/dist/commands/doctor.js +30 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/flags.d.ts +14 -2
- package/dist/commands/flags.js +34 -6
- package/dist/commands/flags.js.map +1 -1
- package/dist/commands/handlers.js +8 -0
- package/dist/commands/handlers.js.map +1 -1
- package/dist/commands/inbox.js +129 -33
- package/dist/commands/inbox.js.map +1 -1
- package/dist/commands/login.d.ts +13 -0
- package/dist/commands/login.js +35 -27
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/messaging/identity.d.ts +9 -0
- package/dist/commands/messaging/identity.js +21 -0
- package/dist/commands/messaging/identity.js.map +1 -1
- package/dist/commands/messaging/validate.d.ts +6 -5
- package/dist/commands/messaging/validate.js +7 -16
- package/dist/commands/messaging/validate.js.map +1 -1
- package/dist/commands/registry.js +88 -8
- package/dist/commands/registry.js.map +1 -1
- package/dist/commands/setup.d.ts +26 -0
- package/dist/commands/setup.js +221 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/task.js +21 -24
- package/dist/commands/task.js.map +1 -1
- package/dist/commands/teach.d.ts +13 -0
- package/dist/commands/teach.js +317 -0
- package/dist/commands/teach.js.map +1 -0
- package/dist/commands/update-check.d.ts +49 -0
- package/dist/commands/update-check.js +203 -0
- package/dist/commands/update-check.js.map +1 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +178 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/use.d.ts +2 -0
- package/dist/commands/use.js +99 -0
- package/dist/commands/use.js.map +1 -0
- package/dist/commands/whoami.js +113 -27
- package/dist/commands/whoami.js.map +1 -1
- package/dist/config-file.js +4 -20
- package/dist/config-file.js.map +1 -1
- package/dist/config.d.ts +15 -1
- package/dist/config.js +43 -4
- package/dist/config.js.map +1 -1
- package/dist/exit-codes.d.ts +6 -0
- package/dist/exit-codes.js +6 -0
- package/dist/exit-codes.js.map +1 -1
- package/dist/help.js +5 -1
- package/dist/help.js.map +1 -1
- package/dist/onboarding/open-url.d.ts +26 -0
- package/dist/onboarding/open-url.js +69 -0
- package/dist/onboarding/open-url.js.map +1 -0
- package/dist/onboarding/status.d.ts +24 -0
- package/dist/onboarding/status.js +53 -0
- package/dist/onboarding/status.js.map +1 -0
- package/dist/os.d.ts +16 -0
- package/dist/os.js +32 -0
- package/dist/os.js.map +1 -0
- package/dist/url.d.ts +8 -0
- package/dist/url.js +29 -0
- package/dist/url.js.map +1 -0
- package/dist/version.d.ts +11 -3
- package/dist/version.js +20 -3
- package/dist/version.js.map +1 -1
- package/dist/watch-lock.d.ts +85 -0
- package/dist/watch-lock.js +358 -0
- package/dist/watch-lock.js.map +1 -0
- package/package.json +2 -2
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { type Binding } from "./bindings.js";
|
|
2
|
+
export declare const DEFAULT_BASE_URL = "https://inbox.ohhai.app";
|
|
2
3
|
export type OutputFormat = "human" | "json";
|
|
3
4
|
export type TokenSource = "env" | "keychain" | "file" | "none";
|
|
5
|
+
/** Where the resolved `account` came from (§6 precedence), for `whoami` to explain the resolution.
|
|
6
|
+
* `"binding"` is a user-side directory→agent binding (bindings.ts); `"none"` means nothing was
|
|
7
|
+
* configured — a downstream sole-identity DISCOVERY (resolveIdentity) may still find one. */
|
|
8
|
+
export type AccountSource = "flag" | "env" | "binding" | "user-config" | "none";
|
|
4
9
|
/** Global flags after parsing (the subset that feeds config resolution). */
|
|
5
10
|
export interface RawFlags {
|
|
6
11
|
baseUrl?: string | undefined;
|
|
@@ -45,12 +50,21 @@ export interface ResolvedConfig {
|
|
|
45
50
|
color: boolean;
|
|
46
51
|
token: string | undefined;
|
|
47
52
|
tokenSource: TokenSource;
|
|
53
|
+
/** How `account` resolved (§6), for `whoami`. Optional so a hand-built config (tests) may omit it. */
|
|
54
|
+
accountSource?: AccountSource;
|
|
55
|
+
/** The bound directory when `accountSource === "binding"`; omitted otherwise. */
|
|
56
|
+
bindingPath?: string | undefined;
|
|
48
57
|
}
|
|
49
58
|
export interface ResolveInput {
|
|
50
59
|
flags?: RawFlags | undefined;
|
|
51
60
|
env?: EnvVars | undefined;
|
|
52
61
|
userConfig?: UserConfig | undefined;
|
|
53
62
|
projectConfig?: ProjectConfig | undefined;
|
|
63
|
+
/** User-side directory→agent bindings (already loaded from the user dir; bindings.ts). Consulted for
|
|
64
|
+
* the account precedence only — a repo never reaches this, preserving credential-safety (§6). */
|
|
65
|
+
bindings?: Binding[] | undefined;
|
|
66
|
+
/** The process working directory, for walk-up binding resolution. Omitted → bindings are ignored. */
|
|
67
|
+
cwd?: string | undefined;
|
|
54
68
|
}
|
|
55
69
|
export interface ResolveResult {
|
|
56
70
|
config: ResolvedConfig;
|
package/dist/config.js
CHANGED
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
//
|
|
12
12
|
// #105 implements the resolution CONTRACT over an already-parsed, in-memory config object.
|
|
13
13
|
// The TOML file loader + per-project discovery + keychain read land in #106/#107.
|
|
14
|
-
|
|
14
|
+
import { resolveBinding } from "./bindings.js";
|
|
15
|
+
import { originOf } from "./url.js";
|
|
16
|
+
// The production Hub — a shipped CLI must reach the real service with zero configuration (#271, R8).
|
|
17
|
+
// Local development points at a local hub by setting MA2H_BASE_URL (or `--base-url`), which outranks
|
|
18
|
+
// this default (§6 precedence).
|
|
19
|
+
export const DEFAULT_BASE_URL = "https://inbox.ohhai.app";
|
|
15
20
|
/** A string is "present" only when non-empty after trimming; the trimmed value is returned
|
|
16
21
|
* so stray whitespace never rides along into a base URL / account / token. */
|
|
17
22
|
function presentString(value) {
|
|
@@ -63,8 +68,42 @@ export function resolveConfig(input = {}) {
|
|
|
63
68
|
// empty, so fall back to the default rather than yield an unusable "".
|
|
64
69
|
const resolvedBase = (firstPresent(flags.baseUrl, env.MA2H_BASE_URL, userConfig.base_url) ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
65
70
|
const baseUrl = resolvedBase === "" ? DEFAULT_BASE_URL : resolvedBase;
|
|
66
|
-
// account: flag > env > USER config only. Project config excluded on purpose.
|
|
67
|
-
|
|
71
|
+
// account: flag > env > directory binding > USER config only (§6). Project config excluded on purpose.
|
|
72
|
+
// The directory binding (bindings.ts) is user-side + origin-scoped, so it can supply the account for
|
|
73
|
+
// the current directory tree WITHOUT a repo ever influencing identity — it sits BELOW an explicit
|
|
74
|
+
// --account / MA2H_AGENT_ID (which still win) and ABOVE the user-global default. `accountSource`
|
|
75
|
+
// records which layer won, for `whoami` to explain the resolution.
|
|
76
|
+
const origin = originOf(baseUrl);
|
|
77
|
+
const binding = input.bindings !== undefined && input.cwd !== undefined
|
|
78
|
+
? resolveBinding(input.bindings, input.cwd, origin)
|
|
79
|
+
: undefined;
|
|
80
|
+
const flagAccount = presentString(flags.account);
|
|
81
|
+
const envAccount = presentString(env.MA2H_AGENT_ID);
|
|
82
|
+
const userAccount = presentString(userConfig.default_account);
|
|
83
|
+
let account;
|
|
84
|
+
let accountSource;
|
|
85
|
+
let bindingPath = undefined;
|
|
86
|
+
if (flagAccount !== undefined) {
|
|
87
|
+
account = flagAccount;
|
|
88
|
+
accountSource = "flag";
|
|
89
|
+
}
|
|
90
|
+
else if (envAccount !== undefined) {
|
|
91
|
+
account = envAccount;
|
|
92
|
+
accountSource = "env";
|
|
93
|
+
}
|
|
94
|
+
else if (binding !== undefined) {
|
|
95
|
+
account = binding.account;
|
|
96
|
+
accountSource = "binding";
|
|
97
|
+
bindingPath = binding.path;
|
|
98
|
+
}
|
|
99
|
+
else if (userAccount !== undefined) {
|
|
100
|
+
account = userAccount;
|
|
101
|
+
accountSource = "user-config";
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
account = undefined;
|
|
105
|
+
accountSource = "none";
|
|
106
|
+
}
|
|
68
107
|
// output: an explicit --json flag wins EITHER way (--json → json, --json=false → human, upholding
|
|
69
108
|
// flag > config precedence); only when the flag is absent does the config `output` decide; else human.
|
|
70
109
|
const output = flags.json === true
|
|
@@ -90,7 +129,7 @@ export function resolveConfig(input = {}) {
|
|
|
90
129
|
const envToken = presentString(env.MA2H_AGENT_TOKEN);
|
|
91
130
|
const tokenSource = envToken !== undefined ? "env" : "none";
|
|
92
131
|
return {
|
|
93
|
-
config: { baseUrl, account, output, timeoutMs, color, token: envToken, tokenSource },
|
|
132
|
+
config: { baseUrl, account, output, timeoutMs, color, token: envToken, tokenSource, accountSource, bindingPath },
|
|
94
133
|
warnings,
|
|
95
134
|
};
|
|
96
135
|
}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,EAAE;AACF,sFAAsF;AACtF,EAAE;AACF,sFAAsF;AACtF,2FAA2F;AAC3F,6FAA6F;AAC7F,2FAA2F;AAC3F,2FAA2F;AAC3F,0FAA0F;AAC1F,EAAE;AACF,2FAA2F;AAC3F,kFAAkF;AAElF,MAAM,CAAC,MAAM,gBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/exit-codes.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ export declare const ExitCode: {
|
|
|
19
19
|
readonly CONFLICT: 8;
|
|
20
20
|
/** 9 — Invalid request (Hub 400/422/413; version not supported). */
|
|
21
21
|
readonly INVALID_REQUEST: 9;
|
|
22
|
+
/** 10 — Already running: a LOCAL precondition conflict, no Hub call — currently only a second
|
|
23
|
+
* `inbox watch` on an identity another live watcher already holds (#323). Distinct from `8`
|
|
24
|
+
* (a Hub 409) so a caller branching on the exit code never mistakes a non-retryable "stop the
|
|
25
|
+
* other watcher" for a retryable idempotency conflict. */
|
|
26
|
+
readonly ALREADY_RUNNING: 10;
|
|
22
27
|
};
|
|
23
28
|
export type ExitCode = (typeof ExitCode)[keyof typeof ExitCode];
|
|
24
29
|
/**
|
|
@@ -39,6 +44,7 @@ declare const ERROR_CODE_TO_EXIT: {
|
|
|
39
44
|
readonly validation_error: 9;
|
|
40
45
|
readonly payload_too_large: 9;
|
|
41
46
|
readonly version_not_supported: 9;
|
|
47
|
+
readonly already_running: 10;
|
|
42
48
|
readonly not_implemented: 1;
|
|
43
49
|
};
|
|
44
50
|
export type ErrorCode = keyof typeof ERROR_CODE_TO_EXIT;
|
package/dist/exit-codes.js
CHANGED
|
@@ -22,6 +22,11 @@ export const ExitCode = {
|
|
|
22
22
|
CONFLICT: 8,
|
|
23
23
|
/** 9 — Invalid request (Hub 400/422/413; version not supported). */
|
|
24
24
|
INVALID_REQUEST: 9,
|
|
25
|
+
/** 10 — Already running: a LOCAL precondition conflict, no Hub call — currently only a second
|
|
26
|
+
* `inbox watch` on an identity another live watcher already holds (#323). Distinct from `8`
|
|
27
|
+
* (a Hub 409) so a caller branching on the exit code never mistakes a non-retryable "stop the
|
|
28
|
+
* other watcher" for a retryable idempotency conflict. */
|
|
29
|
+
ALREADY_RUNNING: 10,
|
|
25
30
|
};
|
|
26
31
|
/**
|
|
27
32
|
* Stable `error.code` strings (§8) → exit codes (§7). An exit code may cover more
|
|
@@ -41,6 +46,7 @@ const ERROR_CODE_TO_EXIT = {
|
|
|
41
46
|
validation_error: ExitCode.INVALID_REQUEST,
|
|
42
47
|
payload_too_large: ExitCode.INVALID_REQUEST,
|
|
43
48
|
version_not_supported: ExitCode.INVALID_REQUEST,
|
|
49
|
+
already_running: ExitCode.ALREADY_RUNNING,
|
|
44
50
|
not_implemented: ExitCode.ERROR,
|
|
45
51
|
};
|
|
46
52
|
/** Map a (possibly unknown) `error.code` string to its exit code; unknown → generic 1. */
|
package/dist/exit-codes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exit-codes.js","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mFAAmF;AACnF,gEAAgE;AAEhE,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,mBAAmB;IACnB,OAAO,EAAE,CAAC;IACV,sCAAsC;IACtC,KAAK,EAAE,CAAC;IACR,mEAAmE;IACnE,KAAK,EAAE,CAAC;IACR,mEAAmE;IACnE,IAAI,EAAE,CAAC;IACP,sDAAsD;IACtD,SAAS,EAAE,CAAC;IACZ,oEAAoE;IACpE,OAAO,EAAE,CAAC;IACV,kCAAkC;IAClC,MAAM,EAAE,CAAC;IACT,qEAAqE;IACrE,OAAO,EAAE,CAAC;IACV,4EAA4E;IAC5E,QAAQ,EAAE,CAAC;IACX,oEAAoE;IACpE,eAAe,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"exit-codes.js","sourceRoot":"","sources":["../src/exit-codes.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mFAAmF;AACnF,gEAAgE;AAEhE,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,mBAAmB;IACnB,OAAO,EAAE,CAAC;IACV,sCAAsC;IACtC,KAAK,EAAE,CAAC;IACR,mEAAmE;IACnE,KAAK,EAAE,CAAC;IACR,mEAAmE;IACnE,IAAI,EAAE,CAAC;IACP,sDAAsD;IACtD,SAAS,EAAE,CAAC;IACZ,oEAAoE;IACpE,OAAO,EAAE,CAAC;IACV,kCAAkC;IAClC,MAAM,EAAE,CAAC;IACT,qEAAqE;IACrE,OAAO,EAAE,CAAC;IACV,4EAA4E;IAC5E,QAAQ,EAAE,CAAC;IACX,oEAAoE;IACpE,eAAe,EAAE,CAAC;IAClB;;;+DAG2D;IAC3D,eAAe,EAAE,EAAE;CACX,CAAC;AAIX;;;;GAIG;AACH,MAAM,kBAAkB,GAAG;IACzB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,KAAK,EAAE,QAAQ,CAAC,KAAK;IACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;IACnB,SAAS,EAAE,QAAQ,CAAC,SAAS;IAC7B,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;IACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;IACzB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;IAC3B,WAAW,EAAE,QAAQ,CAAC,eAAe;IACrC,gBAAgB,EAAE,QAAQ,CAAC,eAAe;IAC1C,iBAAiB,EAAE,QAAQ,CAAC,eAAe;IAC3C,qBAAqB,EAAE,QAAQ,CAAC,eAAe;IAC/C,eAAe,EAAE,QAAQ,CAAC,eAAe;IACzC,eAAe,EAAE,QAAQ,CAAC,KAAK;CACvB,CAAC;AAIX,0FAA0F;AAC1F,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,KAAK,GAA6B,kBAAkB,CAAC;IAC3D,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC;AACvC,CAAC;AAED;;oGAEoG;AACpG,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,CAAC;AACxE,CAAC"}
|
package/dist/help.js
CHANGED
|
@@ -5,11 +5,15 @@ export const TOP_LEVEL_HELP = [
|
|
|
5
5
|
"",
|
|
6
6
|
"Usage: oh-hai [global flags] <command> [subcommand] [flags]",
|
|
7
7
|
"",
|
|
8
|
-
"
|
|
8
|
+
"Setup: setup (bare `oh-hai` on a new machine onboards; re-run to add/re-auth)",
|
|
9
|
+
"Auth: login | logout | whoami | use",
|
|
10
|
+
" (use: bind this directory to an agent — per-project identity)",
|
|
9
11
|
"Messaging: notify | ask | task",
|
|
10
12
|
"Inbox: inbox watch",
|
|
11
13
|
"Agents: agents list | create | revoke",
|
|
12
14
|
"Health: doctor",
|
|
15
|
+
"Teach: teach (write the OH HAI snippet into this machine's agent-instruction files)",
|
|
16
|
+
"Update: upgrade (self-update, or --check for a newer oh-hai)",
|
|
13
17
|
"",
|
|
14
18
|
"Global flags: --base-url --account --json --quiet --verbose --no-color --timeout",
|
|
15
19
|
"Run 'oh-hai <command> --help' for details.",
|
package/dist/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,qEAAqE;AAErE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,8DAA8D;IAC9D,EAAE;IACF,6DAA6D;IAC7D,EAAE;IACF,
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,qEAAqE;AAErE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,8DAA8D;IAC9D,EAAE;IACF,6DAA6D;IAC7D,EAAE;IACF,sFAAsF;IACtF,2CAA2C;IAC3C,mFAAmF;IACnF,iCAAiC;IACjC,yBAAyB;IACzB,2CAA2C;IAC3C,oBAAoB;IACpB,4FAA4F;IAC5F,kEAAkE;IAClE,EAAE;IACF,kFAAkF;IAClF,4CAA4C;CAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** The minimal child-process surface this util touches — `once('spawn'|'error')` to learn whether
|
|
2
|
+
* the opener launched, and `unref()` so it doesn't keep the event loop (or the CLI) alive. Node's
|
|
3
|
+
* real `ChildProcess` is structurally assignable, so the production `spawn` fits without a cast. */
|
|
4
|
+
export interface SpawnedOpener {
|
|
5
|
+
once(event: "spawn", listener: () => void): unknown;
|
|
6
|
+
once(event: "error", listener: (err: Error) => void): unknown;
|
|
7
|
+
unref(): void;
|
|
8
|
+
}
|
|
9
|
+
/** The `spawn` shape the util needs (file + argv + detached/ignore options). Injected so tests assert
|
|
10
|
+
* the exact opener + argv without launching anything. Node's `child_process.spawn` is assignable. */
|
|
11
|
+
export type SpawnLike = (file: string, args: string[], options: {
|
|
12
|
+
detached: boolean;
|
|
13
|
+
stdio: "ignore";
|
|
14
|
+
}) => SpawnedOpener;
|
|
15
|
+
export interface OpenUrlDeps {
|
|
16
|
+
/** Override the detected platform (tests exercise darwin / win32 / linux without three machines). */
|
|
17
|
+
platform?: NodeJS.Platform;
|
|
18
|
+
/** Inject the spawn implementation (tests). Defaults to the real `node:child_process` spawn. */
|
|
19
|
+
spawn?: SpawnLike;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Try to open `url` in the user's default browser. Resolves `true` when the opener process
|
|
23
|
+
* launched, `false` when it couldn't (non-http(s) URL, missing opener binary, spawn failure).
|
|
24
|
+
* Never throws — a `false` result is the caller's cue to print the URL + code instead.
|
|
25
|
+
*/
|
|
26
|
+
export declare function openInBrowser(url: string, deps?: OpenUrlDeps): Promise<boolean>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Best-effort browser opener for the onboarding flow (docs/specs/cli.md §4; issue #271). The
|
|
2
|
+
// device-code login prompts a human to APPROVE in their browser; "platinum" onboarding opens that
|
|
3
|
+
// URL for them instead of making them copy it. This is deliberately fire-and-forget and NON-fatal:
|
|
4
|
+
//
|
|
5
|
+
// - It launches the platform's own opener via `node:child_process` (no runtime dependency, mirroring
|
|
6
|
+
// the keychain backend's shell-out): macOS `open`, Windows `cmd /c start`, else `xdg-open`.
|
|
7
|
+
// - It returns whether the opener process LAUNCHED — never whether the human finished anything.
|
|
8
|
+
// A missing opener (headless box, no `xdg-open`), an unknown platform, or a non-http(s) URL all
|
|
9
|
+
// resolve `false` so the caller falls back to PRINTING the URL + short code. It never throws.
|
|
10
|
+
// - The child is spawned DETACHED with `stdio: "ignore"` and `unref()`'d so the CLI can exit
|
|
11
|
+
// immediately after; we never wait on the browser or read its output.
|
|
12
|
+
//
|
|
13
|
+
// SECURITY: only http(s) URLs are ever handed to the opener. The verification URL is server-supplied
|
|
14
|
+
// (login.ts already validates the scheme), so this is defense-in-depth — a `file:`/`javascript:` URL
|
|
15
|
+
// must never reach a shell-less `open`/`xdg-open` as a thing to "open".
|
|
16
|
+
import { spawn } from "node:child_process";
|
|
17
|
+
import { isHttpUrl } from "../url.js";
|
|
18
|
+
/** The opener command + argv for a platform. On Windows we deliberately do NOT use `cmd /c start`:
|
|
19
|
+
* even a shell-less spawn there launches `cmd.exe`, whose OWN parser re-interprets `& | < > ^ ( )`
|
|
20
|
+
* before argv splitting — and the URL is Hub-supplied (`verification_uri[_complete]`), guarded only
|
|
21
|
+
* by a scheme check, so `https://h/?x=1&calc.exe` would let a hostile Hub run a local command during
|
|
22
|
+
* onboarding. `rundll32 url.dll,FileProtocolHandler <url>` hands the URL to the OS default-handler as
|
|
23
|
+
* a SINGLE argv element with no command interpreter in the path. `open`/`xdg-open` already take the
|
|
24
|
+
* URL as one argv element (no shell), so they're unaffected. */
|
|
25
|
+
function openerFor(platform, url) {
|
|
26
|
+
if (platform === "darwin")
|
|
27
|
+
return { file: "open", args: [url] };
|
|
28
|
+
if (platform === "win32")
|
|
29
|
+
return { file: "rundll32", args: ["url.dll,FileProtocolHandler", url] };
|
|
30
|
+
return { file: "xdg-open", args: [url] };
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Try to open `url` in the user's default browser. Resolves `true` when the opener process
|
|
34
|
+
* launched, `false` when it couldn't (non-http(s) URL, missing opener binary, spawn failure).
|
|
35
|
+
* Never throws — a `false` result is the caller's cue to print the URL + code instead.
|
|
36
|
+
*/
|
|
37
|
+
export function openInBrowser(url, deps = {}) {
|
|
38
|
+
if (!isHttpUrl(url))
|
|
39
|
+
return Promise.resolve(false);
|
|
40
|
+
const platform = deps.platform ?? process.platform;
|
|
41
|
+
const spawnImpl = deps.spawn ?? spawn;
|
|
42
|
+
const { file, args } = openerFor(platform, url);
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
let settled = false;
|
|
45
|
+
const done = (value) => {
|
|
46
|
+
if (!settled) {
|
|
47
|
+
settled = true;
|
|
48
|
+
resolve(value);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
try {
|
|
52
|
+
// Detached + ignored stdio so the opener outlives the CLI and never blocks on I/O.
|
|
53
|
+
const child = spawnImpl(file, args, { detached: true, stdio: "ignore" });
|
|
54
|
+
// `spawn` fires once the child process actually started; `error` fires on ENOENT (opener
|
|
55
|
+
// missing) or a spawn failure. Either resolves the promise exactly once.
|
|
56
|
+
child.once("spawn", () => {
|
|
57
|
+
child.unref();
|
|
58
|
+
done(true);
|
|
59
|
+
});
|
|
60
|
+
child.once("error", () => done(false));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// A synchronous throw from spawn (e.g. an invalid options combination) is still just "couldn't
|
|
64
|
+
// open" — degrade to the print fallback rather than failing onboarding.
|
|
65
|
+
done(false);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=open-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-url.js","sourceRoot":"","sources":["../../src/onboarding/open-url.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,kGAAkG;AAClG,mGAAmG;AACnG,EAAE;AACF,uGAAuG;AACvG,gGAAgG;AAChG,kGAAkG;AAClG,oGAAoG;AACpG,kGAAkG;AAClG,+FAA+F;AAC/F,0EAA0E;AAC1E,EAAE;AACF,qGAAqG;AACrG,qGAAqG;AACrG,wEAAwE;AAExE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AA0BtC;;;;;;iEAMiE;AACjE,SAAS,SAAS,CAAC,QAAyB,EAAE,GAAW;IACvD,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAChE,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,6BAA6B,EAAE,GAAG,CAAC,EAAE,CAAC;IAClG,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW,EAAE,OAAoB,EAAE;IAC/D,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;QAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACnD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,IAAK,KAA8B,CAAC;IAChE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAEhD,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACtC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,GAAG,CAAC,KAAc,EAAQ,EAAE;YACpC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,GAAG,IAAI,CAAC;gBACf,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,CAAC;QACH,CAAC,CAAC;QACF,IAAI,CAAC;YACH,mFAAmF;YACnF,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YACzE,yFAAyF;YACzF,yEAAyE;YACzE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;gBACvB,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,CAAC,CAAC,CAAC;YACH,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,+FAA+F;YAC/F,wEAAwE;YACxE,IAAI,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "../config.js";
|
|
2
|
+
import type { CredentialStore } from "../auth/token-store.js";
|
|
3
|
+
export interface MachineStatus {
|
|
4
|
+
/** True when this machine already has a usable credential for the resolved Hub origin. */
|
|
5
|
+
setUp: boolean;
|
|
6
|
+
/** Where the credential came from: the env token, a stored identity, or nothing. */
|
|
7
|
+
source: "env" | "stored" | "none";
|
|
8
|
+
/** The agent id to display, when unambiguous — the env / configured account, or a sole stored
|
|
9
|
+
* identity. Undefined when there is no single one to name (no account on the env token, or multiple
|
|
10
|
+
* stored identities discovered). */
|
|
11
|
+
agentId: string | undefined;
|
|
12
|
+
/** How many resolvable stored identities exist for this origin (0 on the env / configured-account
|
|
13
|
+
* paths, which resolve a single specific credential rather than discovering). */
|
|
14
|
+
storedCount: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve whether this machine is set up for the config's Hub, and how, WITHOUT revealing a token.
|
|
18
|
+
* `openStore` is a thunk so the store is opened only when a local lookup is actually needed — the env
|
|
19
|
+
* credential proves set-up with no keychain probe at all (important in CI / locked-keychain envs). The
|
|
20
|
+
* resolution precedence mirrors `resolveIdentity` (resolve-token.ts) exactly, so a machine that reads
|
|
21
|
+
* as "set up" here is one whose credential `notify`/`ask` will actually resolve:
|
|
22
|
+
* env token > the explicitly-configured account's token > the sole discoverable stored identity.
|
|
23
|
+
*/
|
|
24
|
+
export declare function machineStatus(config: ResolvedConfig, openStore: () => Promise<CredentialStore>): Promise<MachineStatus>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// "Is this machine set up?" — shared by `oh-hai setup` (#271) and the bare-`oh-hai` first-run
|
|
2
|
+
// dispatch (cli.ts). A machine is set up for a Hub when it already has a usable credential for that
|
|
3
|
+
// Hub's ORIGIN (§5.1 Hub-scoping): either the `MA2H_AGENT_TOKEN` env credential (the CI path) or at
|
|
4
|
+
// least one stored identity in the keychain/file backend whose bearer still resolves.
|
|
5
|
+
//
|
|
6
|
+
// This mirrors resolveIdentity()'s "resolvable stored identity" logic (resolve-token.ts): `list()`
|
|
7
|
+
// reads a keys-only index (redaction-safe — it can drift from the backend), so an identity counts
|
|
8
|
+
// only when its token actually resolves. We count resolvable identities to decide set-up + whether
|
|
9
|
+
// there's a single unambiguous one to name in the "connected as …" message.
|
|
10
|
+
import { originOf } from "../auth/resolve-token.js";
|
|
11
|
+
/**
|
|
12
|
+
* Resolve whether this machine is set up for the config's Hub, and how, WITHOUT revealing a token.
|
|
13
|
+
* `openStore` is a thunk so the store is opened only when a local lookup is actually needed — the env
|
|
14
|
+
* credential proves set-up with no keychain probe at all (important in CI / locked-keychain envs). The
|
|
15
|
+
* resolution precedence mirrors `resolveIdentity` (resolve-token.ts) exactly, so a machine that reads
|
|
16
|
+
* as "set up" here is one whose credential `notify`/`ask` will actually resolve:
|
|
17
|
+
* env token > the explicitly-configured account's token > the sole discoverable stored identity.
|
|
18
|
+
*/
|
|
19
|
+
export async function machineStatus(config, openStore) {
|
|
20
|
+
// The env credential (CI path, §5.3) is authoritative and needs no store lookup. Its agent id may be
|
|
21
|
+
// absent (a token with no MA2H_AGENT_ID) — still "set up", just unnamed.
|
|
22
|
+
if (config.tokenSource === "env") {
|
|
23
|
+
return { setUp: true, source: "env", agentId: config.account, storedCount: 0 };
|
|
24
|
+
}
|
|
25
|
+
const origin = originOf(config.baseUrl);
|
|
26
|
+
const store = await openStore();
|
|
27
|
+
// An explicitly-configured account (--account / MA2H_AGENT_ID / default_account) wins: set-up means
|
|
28
|
+
// THAT account resolves, not merely that some other identity does — otherwise bare `oh-hai` would
|
|
29
|
+
// print help while `notify` (which looks up only the configured account) fails auth. Mirrors
|
|
30
|
+
// resolveIdentity's "configured account wins, no discovery" branch.
|
|
31
|
+
if (config.account !== undefined) {
|
|
32
|
+
const found = await store.getWithSource(origin, config.account);
|
|
33
|
+
return found !== undefined
|
|
34
|
+
? { setUp: true, source: "stored", agentId: config.account, storedCount: 1 }
|
|
35
|
+
: { setUp: false, source: "none", agentId: undefined, storedCount: 0 };
|
|
36
|
+
}
|
|
37
|
+
// No configured account: discover the resolvable stored identities for this origin (a sole one is
|
|
38
|
+
// unambiguous; several stay ambiguous → no single agent to name, but still "set up").
|
|
39
|
+
const listed = (await store.list()).filter((identity) => identity.origin === origin);
|
|
40
|
+
let count = 0;
|
|
41
|
+
let soleAgent;
|
|
42
|
+
for (const identity of listed) {
|
|
43
|
+
const found = await store.getWithSource(origin, identity.agentId);
|
|
44
|
+
if (found !== undefined) {
|
|
45
|
+
count++;
|
|
46
|
+
soleAgent = identity.agentId; // meaningful only when count ends at 1
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (count === 0)
|
|
50
|
+
return { setUp: false, source: "none", agentId: undefined, storedCount: 0 };
|
|
51
|
+
return { setUp: true, source: "stored", agentId: count === 1 ? soleAgent : undefined, storedCount: count };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/onboarding/status.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,oGAAoG;AACpG,oGAAoG;AACpG,sFAAsF;AACtF,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,mGAAmG;AACnG,4EAA4E;AAI5E,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAgBpD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAsB,EACtB,SAAyC;IAEzC,qGAAqG;IACrG,yEAAyE;IACzE,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IACjF,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAEhC,oGAAoG;IACpG,kGAAkG;IAClG,6FAA6F;IAC7F,oEAAoE;IACpE,IAAI,MAAM,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAChE,OAAO,KAAK,KAAK,SAAS;YACxB,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC,EAAE;YAC5E,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,kGAAkG;IAClG,sFAAsF;IACtF,MAAM,MAAM,GAAG,CAAC,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACrF,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,SAA6B,CAAC;IAClC,KAAK,MAAM,QAAQ,IAAI,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAClE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,EAAE,CAAC;YACR,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,uCAAuC;QACvE,CAAC;IACH,CAAC;IACD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC7F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC;AAC7G,CAAC"}
|
package/dist/os.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Read an OS value that can throw (notably `process.cwd()` when the working dir was removed), degrading
|
|
2
|
+
* to "" so a caller never crashes on an OS value it may not even need. */
|
|
3
|
+
export declare function safeOsValue(read: () => string): string;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the absolute config-home directory (`$XDG_CONFIG_HOME`, else `~/.config`) with the
|
|
6
|
+
* credential-safety discipline the TRUSTED user-side files require: prefer an ABSOLUTE
|
|
7
|
+
* `$XDG_CONFIG_HOME`, else `~/.config` when the home dir is absolute, else undefined. A relative or
|
|
8
|
+
* blank XDG is IGNORED on purpose — a relative path would resolve against cwd and let a repo-committed
|
|
9
|
+
* file masquerade as user-global config (bypassing per-project rejection). This is the single source of
|
|
10
|
+
* truth for that invariant, shared by the user config loader (`config-file.ts`, `config.toml`) and the
|
|
11
|
+
* directory-binding store (`bindings.ts`, `bindings.json`).
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveXdgConfigHome(env: {
|
|
14
|
+
homedir: string;
|
|
15
|
+
xdgConfigHome: string | undefined;
|
|
16
|
+
}): string | undefined;
|
package/dist/os.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Small OS-value helpers shared across the CLI. Kept out of any feature module so a generic guard has a
|
|
2
|
+
// neutral home (not `config-file.ts`, which owns TOML loading, nor a command module).
|
|
3
|
+
import { isAbsolute, join } from "node:path";
|
|
4
|
+
/** Read an OS value that can throw (notably `process.cwd()` when the working dir was removed), degrading
|
|
5
|
+
* to "" so a caller never crashes on an OS value it may not even need. */
|
|
6
|
+
export function safeOsValue(read) {
|
|
7
|
+
try {
|
|
8
|
+
return read();
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return "";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the absolute config-home directory (`$XDG_CONFIG_HOME`, else `~/.config`) with the
|
|
16
|
+
* credential-safety discipline the TRUSTED user-side files require: prefer an ABSOLUTE
|
|
17
|
+
* `$XDG_CONFIG_HOME`, else `~/.config` when the home dir is absolute, else undefined. A relative or
|
|
18
|
+
* blank XDG is IGNORED on purpose — a relative path would resolve against cwd and let a repo-committed
|
|
19
|
+
* file masquerade as user-global config (bypassing per-project rejection). This is the single source of
|
|
20
|
+
* truth for that invariant, shared by the user config loader (`config-file.ts`, `config.toml`) and the
|
|
21
|
+
* directory-binding store (`bindings.ts`, `bindings.json`).
|
|
22
|
+
*/
|
|
23
|
+
export function resolveXdgConfigHome(env) {
|
|
24
|
+
const xdg = env.xdgConfigHome?.trim();
|
|
25
|
+
const home = env.homedir.trim();
|
|
26
|
+
return xdg !== undefined && xdg !== "" && isAbsolute(xdg)
|
|
27
|
+
? xdg
|
|
28
|
+
: home !== "" && isAbsolute(home)
|
|
29
|
+
? join(home, ".config")
|
|
30
|
+
: undefined;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=os.js.map
|
package/dist/os.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"os.js","sourceRoot":"","sources":["../src/os.ts"],"names":[],"mappings":"AAAA,wGAAwG;AACxG,sFAAsF;AAEtF,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE7C;2EAC2E;AAC3E,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAGpC;IACC,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa,EAAE,IAAI,EAAE,CAAC;IACtC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IAChC,OAAO,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,EAAE,IAAI,UAAU,CAAC,GAAG,CAAC;QACvD,CAAC,CAAC,GAAG;QACL,CAAC,CAAC,IAAI,KAAK,EAAE,IAAI,UAAU,CAAC,IAAI,CAAC;YAC/B,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC;YACvB,CAAC,CAAC,SAAS,CAAC;AAClB,CAAC"}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function isHttpUrl(value: string): boolean;
|
|
2
|
+
/** Derive the Hub origin (scheme+host+port) from a base URL, for Hub-scoped keying (cli spec §5.1) —
|
|
3
|
+
* the credential store key `<origin>|<agent id>` and the origin-scoped directory bindings. A single
|
|
4
|
+
* definition shared by the config layer (config.ts, binding-origin resolution) and the auth layer
|
|
5
|
+
* (auth/resolve-token.ts, store keying); housed here so neither layer depends on the other. Falls
|
|
6
|
+
* back to the raw string when the URL can't be parsed (resolveConfig guarantees a valid default, so
|
|
7
|
+
* this is belt-and-suspenders). */
|
|
8
|
+
export declare function originOf(baseUrl: string): string;
|
package/dist/url.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Shared http(s) URL scheme guard. A server-supplied URL is safe to hand to a browser opener, or to
|
|
2
|
+
// accept as a callback / verification URL, only when its scheme is http(s) — never file: / javascript:
|
|
3
|
+
// / data: etc. Consolidated here after the guard reached three copies (login.ts device-code flow,
|
|
4
|
+
// onboarding/open-url.ts, messaging/validate.ts); a scheme check is security-relevant, so it should
|
|
5
|
+
// have one definition and one rationale, not three that can drift apart.
|
|
6
|
+
export function isHttpUrl(value) {
|
|
7
|
+
try {
|
|
8
|
+
const { protocol } = new URL(value);
|
|
9
|
+
return protocol === "http:" || protocol === "https:";
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Derive the Hub origin (scheme+host+port) from a base URL, for Hub-scoped keying (cli spec §5.1) —
|
|
16
|
+
* the credential store key `<origin>|<agent id>` and the origin-scoped directory bindings. A single
|
|
17
|
+
* definition shared by the config layer (config.ts, binding-origin resolution) and the auth layer
|
|
18
|
+
* (auth/resolve-token.ts, store keying); housed here so neither layer depends on the other. Falls
|
|
19
|
+
* back to the raw string when the URL can't be parsed (resolveConfig guarantees a valid default, so
|
|
20
|
+
* this is belt-and-suspenders). */
|
|
21
|
+
export function originOf(baseUrl) {
|
|
22
|
+
try {
|
|
23
|
+
return new URL(baseUrl).origin;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return baseUrl;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=url.js.map
|
package/dist/url.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,uGAAuG;AACvG,kGAAkG;AAClG,oGAAoG;AACpG,yEAAyE;AACzE,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,OAAO,QAAQ,KAAK,OAAO,IAAI,QAAQ,KAAK,QAAQ,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;oCAKoC;AACpC,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
/** The MA2H wire protocol version this CLI targets (docs/specs/cli.md §3). */
|
|
2
2
|
export declare const MA2H_WIRE_VERSION = "v0.3";
|
|
3
3
|
/**
|
|
4
|
-
* The CLI's own version
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The CLI's own version.
|
|
5
|
+
*
|
|
6
|
+
* A standalone binary built by `bun build --compile` (issue #344) has no package.json on the
|
|
7
|
+
* real filesystem to read, so the dist workflow inlines the version at compile time: it sets
|
|
8
|
+
* OH_HAI_BUILD_VERSION in the build env and passes `--env='OH_HAI_BUILD_*'`, which freezes this
|
|
9
|
+
* `process.env.OH_HAI_BUILD_VERSION` read into the bundle as a literal. We prefer that value **only
|
|
10
|
+
* when running as the compiled Bun executable** (`process.versions.bun` is set) — so a stray
|
|
11
|
+
* OH_HAI_BUILD_VERSION in the environment can NOT hijack `--version` on the npm/Node path, which
|
|
12
|
+
* stays exactly as before: fall back to reading the manifest relative to this module. That read
|
|
13
|
+
* works both from `dist/version.js` and `src/version.ts` via tsx — both sit one level under the
|
|
14
|
+
* package root, so `../package.json` resolves to the manifest either way.
|
|
7
15
|
*/
|
|
8
16
|
export declare function cliVersion(): string;
|
package/dist/version.js
CHANGED
|
@@ -5,11 +5,28 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
/** The MA2H wire protocol version this CLI targets (docs/specs/cli.md §3). */
|
|
6
6
|
export const MA2H_WIRE_VERSION = "v0.3";
|
|
7
7
|
/**
|
|
8
|
-
* The CLI's own version
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* The CLI's own version.
|
|
9
|
+
*
|
|
10
|
+
* A standalone binary built by `bun build --compile` (issue #344) has no package.json on the
|
|
11
|
+
* real filesystem to read, so the dist workflow inlines the version at compile time: it sets
|
|
12
|
+
* OH_HAI_BUILD_VERSION in the build env and passes `--env='OH_HAI_BUILD_*'`, which freezes this
|
|
13
|
+
* `process.env.OH_HAI_BUILD_VERSION` read into the bundle as a literal. We prefer that value **only
|
|
14
|
+
* when running as the compiled Bun executable** (`process.versions.bun` is set) — so a stray
|
|
15
|
+
* OH_HAI_BUILD_VERSION in the environment can NOT hijack `--version` on the npm/Node path, which
|
|
16
|
+
* stays exactly as before: fall back to reading the manifest relative to this module. That read
|
|
17
|
+
* works both from `dist/version.js` and `src/version.ts` via tsx — both sit one level under the
|
|
18
|
+
* package root, so `../package.json` resolves to the manifest either way.
|
|
11
19
|
*/
|
|
12
20
|
export function cliVersion() {
|
|
21
|
+
// Build-time injected version — trusted ONLY under Bun (the compiled binary), never on the
|
|
22
|
+
// Node/npm path, so an ambient OH_HAI_BUILD_VERSION can't spoof the npm CLI's version. In the
|
|
23
|
+
// compiled binary `process.env.OH_HAI_BUILD_VERSION` is the frozen `--env` literal, not a live
|
|
24
|
+
// read. Empty string is not a valid version, so an unset/blank value falls through to the manifest.
|
|
25
|
+
const runningUnderBun = typeof process.versions.bun === "string";
|
|
26
|
+
const injected = process.env.OH_HAI_BUILD_VERSION;
|
|
27
|
+
if (runningUnderBun && typeof injected === "string" && injected.length > 0) {
|
|
28
|
+
return injected;
|
|
29
|
+
}
|
|
13
30
|
try {
|
|
14
31
|
const manifestUrl = new URL("../package.json", import.meta.url);
|
|
15
32
|
const raw = readFileSync(fileURLToPath(manifestUrl), "utf8");
|
package/dist/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,4FAA4F;AAE5F,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,4FAA4F;AAE5F,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,8EAA8E;AAC9E,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU;IACxB,2FAA2F;IAC3F,8FAA8F;IAC9F,+FAA+F;IAC/F,oGAAoG;IACpG,MAAM,eAAe,GAAG,OAAQ,OAAO,CAAC,QAA6B,CAAC,GAAG,KAAK,QAAQ,CAAC;IACvF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;IAClD,IAAI,eAAe,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3E,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChE,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;QACxD,OAAO,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC"}
|