@oh-hai/cli 0.1.0-beta.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.
Files changed (110) hide show
  1. package/README.md +154 -0
  2. package/dist/auth/file-backend.d.ts +16 -0
  3. package/dist/auth/file-backend.js +98 -0
  4. package/dist/auth/file-backend.js.map +1 -0
  5. package/dist/auth/keychain.d.ts +54 -0
  6. package/dist/auth/keychain.js +232 -0
  7. package/dist/auth/keychain.js.map +1 -0
  8. package/dist/auth/resolve-token.d.ts +34 -0
  9. package/dist/auth/resolve-token.js +91 -0
  10. package/dist/auth/resolve-token.js.map +1 -0
  11. package/dist/auth/secure-write.d.ts +2 -0
  12. package/dist/auth/secure-write.js +30 -0
  13. package/dist/auth/secure-write.js.map +1 -0
  14. package/dist/auth/token-store.d.ts +104 -0
  15. package/dist/auth/token-store.js +208 -0
  16. package/dist/auth/token-store.js.map +1 -0
  17. package/dist/cli.d.ts +16 -0
  18. package/dist/cli.js +238 -0
  19. package/dist/cli.js.map +1 -0
  20. package/dist/commands/agents.d.ts +2 -0
  21. package/dist/commands/agents.js +370 -0
  22. package/dist/commands/agents.js.map +1 -0
  23. package/dist/commands/ask.d.ts +2 -0
  24. package/dist/commands/ask.js +246 -0
  25. package/dist/commands/ask.js.map +1 -0
  26. package/dist/commands/context.d.ts +72 -0
  27. package/dist/commands/context.js +7 -0
  28. package/dist/commands/context.js.map +1 -0
  29. package/dist/commands/doctor.d.ts +2 -0
  30. package/dist/commands/doctor.js +237 -0
  31. package/dist/commands/doctor.js.map +1 -0
  32. package/dist/commands/flags.d.ts +25 -0
  33. package/dist/commands/flags.js +100 -0
  34. package/dist/commands/flags.js.map +1 -0
  35. package/dist/commands/handlers.d.ts +2 -0
  36. package/dist/commands/handlers.js +26 -0
  37. package/dist/commands/handlers.js.map +1 -0
  38. package/dist/commands/http.d.ts +8 -0
  39. package/dist/commands/http.js +19 -0
  40. package/dist/commands/http.js.map +1 -0
  41. package/dist/commands/inbox.d.ts +2 -0
  42. package/dist/commands/inbox.js +111 -0
  43. package/dist/commands/inbox.js.map +1 -0
  44. package/dist/commands/login.d.ts +2 -0
  45. package/dist/commands/login.js +272 -0
  46. package/dist/commands/login.js.map +1 -0
  47. package/dist/commands/logout.d.ts +2 -0
  48. package/dist/commands/logout.js +35 -0
  49. package/dist/commands/logout.js.map +1 -0
  50. package/dist/commands/messaging/await.d.ts +43 -0
  51. package/dist/commands/messaging/await.js +125 -0
  52. package/dist/commands/messaging/await.js.map +1 -0
  53. package/dist/commands/messaging/build.d.ts +46 -0
  54. package/dist/commands/messaging/build.js +66 -0
  55. package/dist/commands/messaging/build.js.map +1 -0
  56. package/dist/commands/messaging/http.d.ts +22 -0
  57. package/dist/commands/messaging/http.js +270 -0
  58. package/dist/commands/messaging/http.js.map +1 -0
  59. package/dist/commands/messaging/identity.d.ts +29 -0
  60. package/dist/commands/messaging/identity.js +63 -0
  61. package/dist/commands/messaging/identity.js.map +1 -0
  62. package/dist/commands/messaging/shared.d.ts +53 -0
  63. package/dist/commands/messaging/shared.js +135 -0
  64. package/dist/commands/messaging/shared.js.map +1 -0
  65. package/dist/commands/messaging/state.d.ts +26 -0
  66. package/dist/commands/messaging/state.js +82 -0
  67. package/dist/commands/messaging/state.js.map +1 -0
  68. package/dist/commands/messaging/validate.d.ts +40 -0
  69. package/dist/commands/messaging/validate.js +193 -0
  70. package/dist/commands/messaging/validate.js.map +1 -0
  71. package/dist/commands/messaging/wire.d.ts +133 -0
  72. package/dist/commands/messaging/wire.js +16 -0
  73. package/dist/commands/messaging/wire.js.map +1 -0
  74. package/dist/commands/notify.d.ts +2 -0
  75. package/dist/commands/notify.js +68 -0
  76. package/dist/commands/notify.js.map +1 -0
  77. package/dist/commands/registry.d.ts +14 -0
  78. package/dist/commands/registry.js +144 -0
  79. package/dist/commands/registry.js.map +1 -0
  80. package/dist/commands/stub.d.ts +1 -0
  81. package/dist/commands/stub.js +9 -0
  82. package/dist/commands/stub.js.map +1 -0
  83. package/dist/commands/task.d.ts +2 -0
  84. package/dist/commands/task.js +223 -0
  85. package/dist/commands/task.js.map +1 -0
  86. package/dist/commands/whoami.d.ts +6 -0
  87. package/dist/commands/whoami.js +90 -0
  88. package/dist/commands/whoami.js.map +1 -0
  89. package/dist/config-file.d.ts +38 -0
  90. package/dist/config-file.js +233 -0
  91. package/dist/config-file.js.map +1 -0
  92. package/dist/config.d.ts +64 -0
  93. package/dist/config.js +97 -0
  94. package/dist/config.js.map +1 -0
  95. package/dist/envelope.d.ts +25 -0
  96. package/dist/envelope.js +41 -0
  97. package/dist/envelope.js.map +1 -0
  98. package/dist/exit-codes.d.ts +51 -0
  99. package/dist/exit-codes.js +57 -0
  100. package/dist/exit-codes.js.map +1 -0
  101. package/dist/help.d.ts +1 -0
  102. package/dist/help.js +17 -0
  103. package/dist/help.js.map +1 -0
  104. package/dist/terminal.d.ts +5 -0
  105. package/dist/terminal.js +18 -0
  106. package/dist/terminal.js.map +1 -0
  107. package/dist/version.d.ts +8 -0
  108. package/dist/version.js +23 -0
  109. package/dist/version.js.map +1 -0
  110. package/package.json +38 -0
@@ -0,0 +1,82 @@
1
+ // Agent-owned sealed `--state` resume for the productized messaging commands (cli spec §4.4).
2
+ //
3
+ // The AES-256-GCM state seal is VENDORED crypto (AGENTS.md — do not reimplement): this module
4
+ // only reads the agent-owned key and wraps `@oh-hai/ma2h-core/state-seal`'s sealState/openState.
5
+ // Consuming the shared core at runtime is why #219 makes `@oh-hai/ma2h-core` publishable — the
6
+ // compiled `dist/cli.js` bin resolves the core's compiled dist. Mirrors the scripts layer
7
+ // (scripts/ask.ts readSealKey + scripts/agent-client.ts sealResume/openSealedState).
8
+ import { openState, sealState } from "@oh-hai/ma2h-core/state-seal";
9
+ import { CliError } from "../../envelope.js";
10
+ /** The env var carrying the agent's 32-byte state-seal key (base64url). Agent-runtime-provisioned,
11
+ * Hub-invisible, distinct from the bearer, and never embedded in `state` (spec §9.3). */
12
+ export const STATE_SEAL_KEY_ENV = "MA2H_STATE_SEAL_KEY";
13
+ /** Read + validate the agent state-seal key from the env. Returns undefined when unset; throws a
14
+ * usage error (exit 2) when set but not exactly 32 bytes. */
15
+ export function readSealKey() {
16
+ const raw = process.env[STATE_SEAL_KEY_ENV]?.trim();
17
+ if (raw === undefined || raw === "")
18
+ return undefined;
19
+ const key = Buffer.from(raw, "base64url");
20
+ if (key.length !== 32) {
21
+ throw new CliError("usage", `${STATE_SEAL_KEY_ENV} must decode to 32 bytes (base64url); got ${key.length}.`);
22
+ }
23
+ return key;
24
+ }
25
+ /** Wrap an agent-owned resume object as a sealed envelope `state` value (`{ sealed: "MA2HSEALv1.…" }`).
26
+ * The seal key is Hub-invisible (spec §9.3). */
27
+ export function sealResumeState(resume, key) {
28
+ return { sealed: sealState(resume, key) };
29
+ }
30
+ /** Open the agent-owned sealed `state` round-tripped in a Response (`{ sealed: "MA2HSEALv1.…" }`).
31
+ * Returns undefined when the Response carries no sealed state. Throws on tamper / wrong key. */
32
+ export function openSealedState(response, key) {
33
+ const sealed = response.state?.["sealed"];
34
+ if (typeof sealed !== "string")
35
+ return undefined;
36
+ return openState(sealed, key);
37
+ }
38
+ /** Await-side opener shared by `ask await` / `task await`: when the resolved message carries a
39
+ * sealed `state` AND `MA2H_STATE_SEAL_KEY` is set, open it. Returns undefined when there is no
40
+ * sealed blob or the key is unset (opening is opt-in — a missing key is not an error). Only the
41
+ * presence of a sealed blob makes the key relevant, so the key is read *after* confirming there is
42
+ * something to open: a set-but-malformed key then fails only the awaits that actually need to open
43
+ * state (a usage error, exit 2) instead of breaking every await. An open failure on a present
44
+ * blob (tamper / wrong key) is a generic error (non-zero) rather than a silent drop of the resume
45
+ * context. (This defers the key check the scripts layer does eagerly — a deliberate CLI nicety.) */
46
+ export function openResumeState(body) {
47
+ const sealed = body.response?.state?.["sealed"];
48
+ if (typeof sealed !== "string")
49
+ return undefined;
50
+ const key = readSealKey();
51
+ if (key === undefined)
52
+ return undefined;
53
+ try {
54
+ return openState(sealed, key);
55
+ }
56
+ catch (err) {
57
+ throw new CliError("error", `failed to open sealed state: ${err instanceof Error ? err.message : String(err)}`);
58
+ }
59
+ }
60
+ /** Submit-side resolver shared by notify / ask / task: parse the `--state` flag (must be a JSON
61
+ * object), require the seal key, and return the sealed envelope `state`. Returns undefined when
62
+ * `--state` is absent. A non-object payload or a missing key is a usage error (exit 2). */
63
+ export function resolveState(raw) {
64
+ if (raw === undefined)
65
+ return undefined;
66
+ let parsed;
67
+ try {
68
+ parsed = JSON.parse(raw);
69
+ }
70
+ catch (err) {
71
+ throw new CliError("usage", `--state is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
72
+ }
73
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
74
+ throw new CliError("usage", "--state must be a JSON object.");
75
+ }
76
+ const key = readSealKey();
77
+ if (key === undefined) {
78
+ throw new CliError("usage", `--state requires ${STATE_SEAL_KEY_ENV} (base64url 32-byte key) so resume state can be sealed.`);
79
+ }
80
+ return sealResumeState(parsed, key);
81
+ }
82
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../../../src/commands/messaging/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { Actor, AskMessage, Priority, RequestMode, ResponseOption, TaskMessage } from "./wire.js";
2
+ export declare const PRIORITIES: readonly Priority[];
3
+ export declare const REQUEST_MODES: readonly RequestMode[];
4
+ export declare const CALLBACK_MODES: readonly ["pull", "push"];
5
+ export type CallbackMode = (typeof CALLBACK_MODES)[number];
6
+ export declare function isPriority(value: string): value is Priority;
7
+ export declare function isRequestMode(value: string): value is RequestMode;
8
+ export declare function isCallbackMode(value: string): value is CallbackMode;
9
+ /** Hub-attested resolver identity `<type>:<id>` with type ∈ {human,agent,system} (spec §9.1).
10
+ * Anchored end-to-end (no embedded whitespace) so a malformed `--resolver` fails loudly here. */
11
+ export declare function isActor(value: string): value is Actor;
12
+ /** Normalize a push `--callback-url` to the canonical WHATWG href the Hub signs and `fetch` POSTs
13
+ * to (so the signed callback_url can't desync from the delivery URL), and enforce http(s) — the
14
+ * Hub's SSRF delivery guard rejects every other scheme, so a mailto:/file: url would pass
15
+ * `new URL()` but silently lose the push. A bad URL / scheme is a usage error (exit 2). */
16
+ export declare function normalizePushUrl(raw: string): string;
17
+ /** Parse `value:label` (label may itself contain ':'). Throws a usage CliError on a bad shape. */
18
+ export declare function parseOption(raw: string): ResponseOption;
19
+ /** A task/ask is resolvable only when allowed_resolvers names ≥1 concrete `human:<id>` — the
20
+ * resolve route is human-only and fail-closed (spec §5.4), so a message with none (or only
21
+ * agent:/system: resolvers) can never be marked done and sits open until expiry. */
22
+ export declare function hasHumanResolver(resolvers: readonly unknown[] | undefined): boolean;
23
+ /** Stricter than `hasHumanResolver`: requires ≥1 `human:<id>` with a CONCRETE (non-wildcard,
24
+ * non-empty) id. The Hub matches allowed_resolvers literally (`allowed.includes(actor)`), so a
25
+ * captured `human:*` can never match any real actor — the replay guard must reject it exactly as
26
+ * the compose path does, or the replayed ask/task is submitted unresolvable. */
27
+ export declare function hasConcreteHumanResolver(resolvers: readonly unknown[] | undefined): boolean;
28
+ /** Read the `--envelope` source: inline JSON, `@<path>` (local file), or `@-` (stdin, via the
29
+ * injected reader so tests never touch fd 0). */
30
+ export declare function readEnvelopeArg(raw: string, readStdin: () => Promise<string>): Promise<string>;
31
+ /** Validate a parsed value is a well-formed ask envelope before replaying it (spec §4.8) — enough
32
+ * to fail loudly on a wrong paste (a notify/task envelope, truncated JSON, missing idempotency
33
+ * key) without re-doing the Hub's full schema validation. */
34
+ export declare function assertAskEnvelope(value: unknown): AskMessage;
35
+ /** Validate a parsed value is a well-formed task envelope before replaying it (spec §4.8),
36
+ * including an opt-in callback's shape (push needs a valid http(s) url; else pull). */
37
+ export declare function assertTaskEnvelope(value: unknown): TaskMessage;
38
+ /** Extract `allowed_resolvers` from a replayed ask/task envelope (they live at `request.` for
39
+ * ask, `action.` for task) — used by the human-resolver replay guard. */
40
+ export declare function replayResolvers(envelope: AskMessage | TaskMessage): readonly unknown[] | undefined;
@@ -0,0 +1,193 @@
1
+ // Pure validation helpers + `--envelope` replay guards for the messaging commands (spec §4.4 /
2
+ // §4.8). Ported from `scripts/ask.ts` + `scripts/task.ts`'s loud-failure discipline: an
3
+ // out-of-range mode/priority/actor, a malformed option, or a wrong-shaped replayed envelope
4
+ // fails LOCALLY (exit 2) rather than slipping through to a remote 422. No I/O except the
5
+ // `@file` read in `readEnvelopeArg` (a local file); `@-` goes through the injected stdin reader
6
+ // so tests stay hermetic.
7
+ import { readFileSync } from "node:fs";
8
+ import { CliError } from "../../envelope.js";
9
+ export const PRIORITIES = ["low", "normal", "high", "urgent"];
10
+ export const REQUEST_MODES = ["select", "input", "confirm"];
11
+ export const CALLBACK_MODES = ["pull", "push"];
12
+ export function isPriority(value) {
13
+ return PRIORITIES.includes(value);
14
+ }
15
+ export function isRequestMode(value) {
16
+ return REQUEST_MODES.includes(value);
17
+ }
18
+ export function isCallbackMode(value) {
19
+ return CALLBACK_MODES.includes(value);
20
+ }
21
+ /** Hub-attested resolver identity `<type>:<id>` with type ∈ {human,agent,system} (spec §9.1).
22
+ * Anchored end-to-end (no embedded whitespace) so a malformed `--resolver` fails loudly here. */
23
+ export function isActor(value) {
24
+ return /^(human|agent|system):[^\s]+$/.test(value);
25
+ }
26
+ /** True when `raw` parses as an http(s) URL — the only schemes the Hub's SSRF delivery guard
27
+ * accepts for a push callback, so a mailto:/file: url would silently lose the push. */
28
+ function isHttpUrl(raw) {
29
+ try {
30
+ const u = new URL(raw);
31
+ return u.protocol === "http:" || u.protocol === "https:";
32
+ }
33
+ catch {
34
+ return false;
35
+ }
36
+ }
37
+ /** Normalize a push `--callback-url` to the canonical WHATWG href the Hub signs and `fetch` POSTs
38
+ * to (so the signed callback_url can't desync from the delivery URL), and enforce http(s) — the
39
+ * Hub's SSRF delivery guard rejects every other scheme, so a mailto:/file: url would pass
40
+ * `new URL()` but silently lose the push. A bad URL / scheme is a usage error (exit 2). */
41
+ export function normalizePushUrl(raw) {
42
+ let parsed;
43
+ try {
44
+ parsed = new URL(raw.trim());
45
+ }
46
+ catch {
47
+ throw new CliError("usage", `--callback-url is not a valid URL: ${JSON.stringify(raw)}`);
48
+ }
49
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
50
+ throw new CliError("usage", `--callback-url must be http(s) (the Hub rejects other schemes), got ${JSON.stringify(parsed.protocol)}`);
51
+ }
52
+ return parsed.href;
53
+ }
54
+ /** Parse `value:label` (label may itself contain ':'). Throws a usage CliError on a bad shape. */
55
+ export function parseOption(raw) {
56
+ const i = raw.indexOf(":");
57
+ if (i < 0)
58
+ throw new CliError("usage", `--option must be "value:label", got "${raw}"`);
59
+ const value = raw.slice(0, i).trim();
60
+ const label = raw.slice(i + 1).trim();
61
+ if (value === "" || label === "") {
62
+ throw new CliError("usage", `--option must be "value:label" with non-empty parts, got "${raw}"`);
63
+ }
64
+ return { value, label };
65
+ }
66
+ /** A task/ask is resolvable only when allowed_resolvers names ≥1 concrete `human:<id>` — the
67
+ * resolve route is human-only and fail-closed (spec §5.4), so a message with none (or only
68
+ * agent:/system: resolvers) can never be marked done and sits open until expiry. */
69
+ export function hasHumanResolver(resolvers) {
70
+ return (resolvers ?? []).some((r) => typeof r === "string" && r.startsWith("human:"));
71
+ }
72
+ /** Stricter than `hasHumanResolver`: requires ≥1 `human:<id>` with a CONCRETE (non-wildcard,
73
+ * non-empty) id. The Hub matches allowed_resolvers literally (`allowed.includes(actor)`), so a
74
+ * captured `human:*` can never match any real actor — the replay guard must reject it exactly as
75
+ * the compose path does, or the replayed ask/task is submitted unresolvable. */
76
+ export function hasConcreteHumanResolver(resolvers) {
77
+ return (resolvers ?? []).some((r) => {
78
+ if (typeof r !== "string" || !r.startsWith("human:"))
79
+ return false;
80
+ const id = r.slice("human:".length);
81
+ return id !== "" && !id.includes("*");
82
+ });
83
+ }
84
+ /** Read the `--envelope` source: inline JSON, `@<path>` (local file), or `@-` (stdin, via the
85
+ * injected reader so tests never touch fd 0). */
86
+ export async function readEnvelopeArg(raw, readStdin) {
87
+ if (raw === "@-")
88
+ return readStdin();
89
+ if (raw.startsWith("@")) {
90
+ const path = raw.slice(1);
91
+ try {
92
+ return readFileSync(path, "utf8");
93
+ }
94
+ catch (error) {
95
+ // A missing/unreadable local file is a usage mistake (exit 2), not a generic error (exit 1):
96
+ // re-throw as a CliError so the message is actionable rather than a raw Node ENOENT.
97
+ const reason = error instanceof Error ? error.message : String(error);
98
+ throw new CliError("usage", `--envelope: could not read ${JSON.stringify(path)}: ${reason}`);
99
+ }
100
+ }
101
+ return raw;
102
+ }
103
+ /** Validate a parsed value is a well-formed ask envelope before replaying it (spec §4.8) — enough
104
+ * to fail loudly on a wrong paste (a notify/task envelope, truncated JSON, missing idempotency
105
+ * key) without re-doing the Hub's full schema validation. */
106
+ export function assertAskEnvelope(value) {
107
+ const env = asObject(value, "--envelope must be a JSON object (the envelope printed by 'ask submit --dry-run')");
108
+ if (env["type"] !== "ask") {
109
+ throw new CliError("usage", `--envelope must be an ask envelope (type "ask"), got type ${JSON.stringify(env["type"])}`);
110
+ }
111
+ if (typeof env["ma2h_version"] !== "string")
112
+ throw new CliError("usage", "--envelope is missing ma2h_version");
113
+ if (typeof env["idempotency_key"] !== "string" || env["idempotency_key"].trim() === "") {
114
+ throw new CliError("usage", "--envelope is missing the REQUIRED idempotency_key");
115
+ }
116
+ if (typeof env["agent"] !== "object" || env["agent"] === null || Array.isArray(env["agent"])) {
117
+ throw new CliError("usage", "--envelope agent must be an object");
118
+ }
119
+ const request = env["request"];
120
+ if (typeof request !== "object" || request === null || Array.isArray(request)) {
121
+ throw new CliError("usage", "--envelope request must be an object");
122
+ }
123
+ // Validate an ask push callback's scheme (mirrors assertTaskEnvelope): a non-http(s) push url
124
+ // passes new URL() but is silently dropped by the Hub's SSRF guard — fail fast locally.
125
+ const callback = request["callback"];
126
+ if (callback !== undefined) {
127
+ if (typeof callback !== "object" || callback === null || Array.isArray(callback)) {
128
+ throw new CliError("usage", "--envelope request.callback must be an object");
129
+ }
130
+ const mode = callback["mode"];
131
+ if (mode === "push") {
132
+ const url = callback["url"];
133
+ if (typeof url !== "string" || !isHttpUrl(url)) {
134
+ throw new CliError("usage", "--envelope push callback requires a valid http(s) url");
135
+ }
136
+ }
137
+ else if (mode !== "pull") {
138
+ throw new CliError("usage", `--envelope request.callback.mode must be "pull" or "push", got ${JSON.stringify(mode)}`);
139
+ }
140
+ }
141
+ return value;
142
+ }
143
+ /** Validate a parsed value is a well-formed task envelope before replaying it (spec §4.8),
144
+ * including an opt-in callback's shape (push needs a valid http(s) url; else pull). */
145
+ export function assertTaskEnvelope(value) {
146
+ const env = asObject(value, "--envelope must be a JSON object (the envelope printed by 'task submit --dry-run')");
147
+ if (env["type"] !== "task") {
148
+ throw new CliError("usage", `--envelope must be a task envelope (type "task"), got type ${JSON.stringify(env["type"])}`);
149
+ }
150
+ if (typeof env["ma2h_version"] !== "string")
151
+ throw new CliError("usage", "--envelope is missing ma2h_version");
152
+ if (typeof env["idempotency_key"] !== "string" || env["idempotency_key"].trim() === "") {
153
+ throw new CliError("usage", "--envelope is missing the REQUIRED idempotency_key (a blank value collides on the Hub's dedup key)");
154
+ }
155
+ if (typeof env["agent"] !== "object" || env["agent"] === null || Array.isArray(env["agent"])) {
156
+ throw new CliError("usage", "--envelope agent must be an object");
157
+ }
158
+ const action = env["action"];
159
+ if (typeof action !== "object" || action === null || Array.isArray(action)) {
160
+ throw new CliError("usage", "--envelope action must be an object");
161
+ }
162
+ const callback = action["callback"];
163
+ if (callback !== undefined) {
164
+ if (typeof callback !== "object" || callback === null || Array.isArray(callback)) {
165
+ throw new CliError("usage", "--envelope action.callback must be an object");
166
+ }
167
+ const mode = callback["mode"];
168
+ if (mode === "push") {
169
+ const url = callback["url"];
170
+ if (typeof url !== "string" || !isHttpUrl(url)) {
171
+ throw new CliError("usage", "--envelope push callback requires a valid http(s) url");
172
+ }
173
+ }
174
+ else if (mode !== "pull") {
175
+ throw new CliError("usage", `--envelope action.callback.mode must be "pull" or "push", got ${JSON.stringify(mode)}`);
176
+ }
177
+ }
178
+ return value;
179
+ }
180
+ /** Extract `allowed_resolvers` from a replayed ask/task envelope (they live at `request.` for
181
+ * ask, `action.` for task) — used by the human-resolver replay guard. */
182
+ export function replayResolvers(envelope) {
183
+ const holder = envelope.type === "ask" ? envelope.request : envelope.action;
184
+ const resolvers = holder.allowed_resolvers;
185
+ return Array.isArray(resolvers) ? resolvers : undefined;
186
+ }
187
+ function asObject(value, message) {
188
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
189
+ throw new CliError("usage", message);
190
+ }
191
+ return value;
192
+ }
193
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/commands/messaging/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /** Recursive JSON value — for opaque `state` and free-form input answers/schemas. */
2
+ export type JsonValue = null | boolean | number | string | JsonValue[] | {
3
+ [key: string]: JsonValue;
4
+ };
5
+ export type JsonObject = {
6
+ [key: string]: JsonValue;
7
+ };
8
+ export type Priority = "low" | "normal" | "high" | "urgent";
9
+ export type Runtime = "github-actions" | "cli" | "cloud" | "desktop" | "openclaw" | "other";
10
+ export type RequestMode = "select" | "input" | "confirm";
11
+ /** Hub-attested resolver identity: `<type>:<id>` (spec §9.1). */
12
+ export type Actor = `${"human" | "agent" | "system"}:${string}`;
13
+ export interface AgentDescriptor {
14
+ id: string;
15
+ run_id: string;
16
+ runtime: Runtime;
17
+ project?: string;
18
+ }
19
+ export interface ResponseOption {
20
+ value: string;
21
+ label: string;
22
+ description?: string;
23
+ }
24
+ /** Callback, discriminated by mode (push requires a url). The CLI never composes callback
25
+ * `auth`, so it is omitted from the local type. */
26
+ export type Callback = {
27
+ mode: "push";
28
+ url: string;
29
+ } | {
30
+ mode: "pull";
31
+ };
32
+ export interface AskRequest {
33
+ mode: RequestMode;
34
+ options?: ResponseOption[];
35
+ schema?: JsonObject;
36
+ allowed_resolvers?: Actor[];
37
+ callback?: Callback;
38
+ }
39
+ export interface TaskAction {
40
+ instructions: string;
41
+ checklist?: {
42
+ text: string;
43
+ }[];
44
+ allowed_resolvers?: Actor[];
45
+ callback?: Callback;
46
+ }
47
+ interface BaseEnvelope {
48
+ ma2h_version: "0.3";
49
+ created_at: string;
50
+ agent: AgentDescriptor;
51
+ title: string;
52
+ body?: string;
53
+ priority?: Priority;
54
+ tags?: string[];
55
+ /** Agent-owned, agent-integrity-sealed resume blob (`{ sealed: "MA2HSEALv1.…" }`, via `--state`).
56
+ * Opaque to the Hub (MUST NOT inspect/log — spec §9.3). Sealed by `state.ts` on submit. */
57
+ state?: JsonObject;
58
+ }
59
+ export interface NotifyMessage extends BaseEnvelope {
60
+ type: "notify";
61
+ idempotency_key?: string;
62
+ }
63
+ export interface AskMessage extends BaseEnvelope {
64
+ type: "ask";
65
+ idempotency_key: string;
66
+ request: AskRequest;
67
+ }
68
+ export interface TaskMessage extends BaseEnvelope {
69
+ type: "task";
70
+ idempotency_key: string;
71
+ action: TaskAction;
72
+ }
73
+ export type A2hMessage = NotifyMessage | AskMessage | TaskMessage;
74
+ export type AskResolution = "answered" | "declined" | "cancelled" | "expired";
75
+ export type TaskResolution = "completed" | "dismissed" | "expired";
76
+ export type Resolution = AskResolution | TaskResolution;
77
+ export type Status = "open" | "delivered" | Resolution;
78
+ export interface ResponseDetail {
79
+ /** ask only: chosen option value (string) or the input object. Absent for task. */
80
+ value?: string | JsonObject;
81
+ actor: Actor;
82
+ resolved_at: string;
83
+ comment?: string;
84
+ }
85
+ export interface A2hResponse {
86
+ ma2h_version: "0.3";
87
+ in_reply_to: string;
88
+ resolution_id: string;
89
+ resolution: Resolution;
90
+ defaulted?: boolean;
91
+ response?: ResponseDetail;
92
+ state?: JsonObject;
93
+ }
94
+ export interface SubmitAck {
95
+ id: string;
96
+ status: "open" | "delivered";
97
+ poll_url: string;
98
+ review_url?: string;
99
+ }
100
+ /** GET /v1/messages/:id — the stored envelope plus its lifecycle status and terminal Response. */
101
+ export type GetMessageBody = A2hMessage & {
102
+ id: string;
103
+ status: Status;
104
+ response?: A2hResponse;
105
+ };
106
+ /** One human→agent directive drained from the agent's mailbox (spec §13.1). */
107
+ export interface InboundDirective {
108
+ ma2h_version: string;
109
+ type: "directive";
110
+ id: string;
111
+ from: string;
112
+ to: string;
113
+ created_at: string;
114
+ title: string;
115
+ body?: string;
116
+ priority?: Priority;
117
+ tags?: string[];
118
+ context?: JsonValue[];
119
+ expires_at?: string;
120
+ sensitive?: boolean;
121
+ }
122
+ /** One drained directive plus its detached `MA2H-Signature` (spec §8.7 / §9.7). Forwarded verbatim
123
+ * so a runtime that wants defense-in-depth can verify the signature against the Hub key. */
124
+ export interface InboundDelivery {
125
+ directive: InboundDirective;
126
+ signature: string;
127
+ }
128
+ /** POST /v1/inbox/ack — the ids of the directives consumed by this call (spec §14; the server's
129
+ * `ackDirectives` returns the id array, not a count). */
130
+ export interface InboxAckResult {
131
+ acked: string[];
132
+ }
133
+ export {};
@@ -0,0 +1,16 @@
1
+ // The MA2H wire shapes the productized messaging commands compose and read (spec/v0.3).
2
+ //
3
+ // These MIRROR the canonical definitions in `@oh-hai/ma2h-core` (`packages/ma2h-core/src/types.ts`,
4
+ // and the shapes `scripts/agent-client.ts` builds), but are RE-DECLARED locally on purpose:
5
+ // `@oh-hai/cli` ships as a standalone published bin (`bin: dist/cli.js`, `tsconfig.build.json`
6
+ // sets `rootDir: "src"`). This matches the CLI's existing design — `config.ts` re-declares its own
7
+ // types rather than reaching out. Only the subset the CLI composes or reads lives here (no context
8
+ // Parts, permissions, or callback-auth refs — the CLI's push callback is a bare `{ mode, url }`).
9
+ // The server validates every envelope at ingest, so any drift surfaces as a 422/400, never silent
10
+ // corruption. As of #219 the CLI DOES consume the core's runtime crypto (`state-seal` — the
11
+ // vendored AES-256-GCM seal must not be re-implemented, AGENTS.md; see `state.ts`), now that
12
+ // `@oh-hai/ma2h-core` is npm-publishable. These wire TYPES stay locally re-declared (types are
13
+ // compile-time only, erased from `dist/cli.js`); a full type re-export from the core is a separate
14
+ // follow-up, out of #219 scope.
15
+ export {};
16
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../src/commands/messaging/wire.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,EAAE;AACF,oGAAoG;AACpG,4FAA4F;AAC5F,+FAA+F;AAC/F,mGAAmG;AACnG,mGAAmG;AACnG,kGAAkG;AAClG,kGAAkG;AAClG,4FAA4F;AAC5F,6FAA6F;AAC7F,+FAA+F;AAC/F,mGAAmG;AACnG,gCAAgC"}
@@ -0,0 +1,2 @@
1
+ import type { CommandContext } from "./context.js";
2
+ export declare function notifyCommand(ctx: CommandContext): Promise<void>;
@@ -0,0 +1,68 @@
1
+ // `oh-hai notify` (docs/specs/cli.md §4.4) — fire-and-forget notification (status / FYI /
2
+ // digest). The productized sibling of `scripts/notify.ts`: the bearer is resolved account-aware
3
+ // (keychain/file/env, not raw env), output follows the `--json` envelope, and HTTP statuses map
4
+ // to §7 exit codes. No idempotency key — a notify must not be retried (a retry posts a duplicate).
5
+ import { CliError, buildOk, serializeEnvelope } from "../envelope.js";
6
+ import { parseCommandArgs } from "./flags.js";
7
+ import { buildNotify } from "./messaging/build.js";
8
+ import { submitEnvelope } from "./messaging/http.js";
9
+ import { resolveSubmitIdentity } from "./messaging/identity.js";
10
+ import { dryRunFlag, printDryRunEnvelope, printSubmitAck, resolveRunId, stringFlag } from "./messaging/shared.js";
11
+ import { resolveState } from "./messaging/state.js";
12
+ import { PRIORITIES, isPriority } from "./messaging/validate.js";
13
+ export async function notifyCommand(ctx) {
14
+ const { values } = parseCommandArgs(ctx.argv, {
15
+ title: { type: "string" },
16
+ body: { type: "string" },
17
+ priority: { type: "string" },
18
+ tag: { type: "string", multiple: true },
19
+ "dry-run": { type: "boolean" },
20
+ state: { type: "string" },
21
+ });
22
+ // Seal an optional `--state` resume blob up front (requires MA2H_STATE_SEAL_KEY) so the
23
+ // preview under --dry-run shows the sealed envelope. notify has no await/open leg.
24
+ const state = resolveState(stringFlag(values.state));
25
+ const title = stringFlag(values.title);
26
+ if (title === undefined || title.trim() === "") {
27
+ throw new CliError("usage", "missing required flag: --title (must be non-empty).");
28
+ }
29
+ const priority = stringFlag(values.priority);
30
+ if (priority !== undefined && !isPriority(priority)) {
31
+ throw new CliError("usage", `invalid --priority "${priority}" (expected one of: ${PRIORITIES.join(", ")}).`);
32
+ }
33
+ const body = stringFlag(values.body);
34
+ const tags = Array.isArray(values.tag) ? values.tag.filter((t) => typeof t === "string") : [];
35
+ // Strict parse (not `=== true`): the `=`-form `--dry-run=true` still previews, and a malformed
36
+ // value like `--dry-run=treu` fails as usage rather than falling through to a LIVE submit.
37
+ const dryRun = dryRunFlag(values["dry-run"]);
38
+ // Under --dry-run neither a token nor a resolved account is required — the envelope is only
39
+ // previewed. Use a placeholder agent id so the composed envelope stays inspectable. A real
40
+ // submit resolves the bearer + account up front (auth/usage errors before composing).
41
+ let agentId = ctx.config.account ?? "oh-hai/notify-bot";
42
+ let token = ""; // reassigned on the real path below; dry-run returns before it's used
43
+ if (!dryRun) {
44
+ const identity = await resolveSubmitIdentity(ctx);
45
+ agentId = identity.agentId;
46
+ token = identity.token;
47
+ }
48
+ const notify = buildNotify({
49
+ agent: { id: agentId, run_id: resolveRunId() },
50
+ title,
51
+ ...(body !== undefined ? { body } : {}),
52
+ ...(priority !== undefined ? { priority } : {}),
53
+ ...(tags.length > 0 ? { tags } : {}),
54
+ ...(state !== undefined ? { state } : {}),
55
+ });
56
+ if (dryRun) {
57
+ printDryRunEnvelope(ctx, "notify", notify);
58
+ return;
59
+ }
60
+ const ack = await submitEnvelope(ctx, token, notify);
61
+ if (ctx.json) {
62
+ ctx.io.log(serializeEnvelope(buildOk("notify", { id: ack.id })));
63
+ }
64
+ else {
65
+ printSubmitAck(ctx, "notify posted", ack);
66
+ }
67
+ }
68
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/commands/notify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface CommandSpec {
2
+ /** The command token, e.g. "notify" or "agents". */
3
+ name: string;
4
+ /** One-line summary for the top-level help. */
5
+ summary: string;
6
+ /** The downstream issue that implements this command's body. */
7
+ issue: number;
8
+ /** Valid subcommands, if any (e.g. ask → submit | await). */
9
+ subcommands: string[];
10
+ /** Full `--help` text for this command. */
11
+ help: string;
12
+ }
13
+ export declare const COMMANDS: readonly CommandSpec[];
14
+ export declare function findCommand(name: string): CommandSpec | undefined;