@chengchenccc/oh-my-agent 0.2.0-rc.4 → 0.2.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/args.d.ts +6 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +47 -0
- package/dist/cli/gateway-commands.d.ts +12 -0
- package/dist/cli/gateway-commands.d.ts.map +1 -1
- package/dist/cli/gateway-commands.js +11 -6
- package/dist/cli/update-command.d.ts +57 -0
- package/dist/cli/update-command.d.ts.map +1 -0
- package/dist/cli/update-command.js +220 -0
- package/dist/core/gateway/doctor.d.ts +18 -0
- package/dist/core/gateway/doctor.d.ts.map +1 -1
- package/dist/core/gateway/doctor.js +109 -1
- package/dist/core/runtime/agent-event.d.ts +19 -1
- package/dist/core/runtime/agent-event.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-run.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-run.js +43 -8
- package/dist/core/runtime/agent-loop-runner.d.ts.map +1 -1
- package/dist/core/runtime/agent-loop-runner.js +19 -0
- package/dist/core/runtime/agent-loop-types.d.ts +2 -0
- package/dist/core/runtime/agent-loop-types.d.ts.map +1 -1
- package/dist/core/runtime/approval.d.ts +7 -1
- package/dist/core/runtime/approval.d.ts.map +1 -1
- package/dist/core/runtime/approval.js +7 -1
- package/dist/core/runtime/plugin.d.ts +10 -1
- package/dist/core/runtime/plugin.d.ts.map +1 -1
- package/dist/core/runtime/run-runtime.d.ts +14 -0
- package/dist/core/runtime/run-runtime.d.ts.map +1 -1
- package/dist/core/runtime/run-runtime.js +46 -12
- package/dist/core/runtime/runtime-catalog.js +17 -4
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +10 -0
- package/dist/core/tools/eval.d.ts.map +1 -1
- package/dist/core/tools/eval.js +7 -0
- package/dist/core/tools/file-tools.d.ts.map +1 -1
- package/dist/core/tools/file-tools.js +20 -0
- package/dist/core/tools/glob.d.ts.map +1 -1
- package/dist/core/tools/glob.js +5 -0
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +29 -0
- package/dist/core/tools/mcp-mount.d.ts +17 -0
- package/dist/core/tools/mcp-mount.d.ts.map +1 -1
- package/dist/core/tools/mcp-mount.js +31 -8
- package/dist/core/tools/presentation.d.ts +19 -0
- package/dist/core/tools/presentation.d.ts.map +1 -0
- package/dist/core/tools/presentation.js +139 -0
- package/dist/core/update/release.d.ts +46 -0
- package/dist/core/update/release.d.ts.map +1 -0
- package/dist/core/update/release.js +119 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +21 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +23 -5
- package/dist/modes/tui/tui-mode.d.ts.map +1 -1
- package/dist/modes/tui/tui-mode.js +16 -0
- package/dist/modes/tui/tui-seam.d.ts +5 -0
- package/dist/modes/tui/tui-seam.d.ts.map +1 -1
- package/dist/protocol/mapping.d.ts +14 -0
- package/dist/protocol/mapping.d.ts.map +1 -1
- package/dist/protocol/mapping.js +68 -1
- package/package.json +11 -11
package/dist/cli/args.d.ts
CHANGED
|
@@ -20,6 +20,12 @@ export interface CliArgs {
|
|
|
20
20
|
readOnly?: boolean;
|
|
21
21
|
/** `oma gateway <command>`: the gateway domain (backend + web) verbs. */
|
|
22
22
|
gateway?: GatewayCommand;
|
|
23
|
+
/** `oma update [--check] [--version <v>]`: the release updater. */
|
|
24
|
+
update?: boolean;
|
|
25
|
+
/** `oma update --check`: report only. */
|
|
26
|
+
updateCheck?: boolean;
|
|
27
|
+
/** `oma update --version <v>`: pin the target version. */
|
|
28
|
+
updateVersion?: string;
|
|
23
29
|
/** `oma gateway up|fetch --version <v>`: pin the artifact version. */
|
|
24
30
|
gatewayVersion?: string;
|
|
25
31
|
/** `oma gateway up -d`: run detached. */
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAoB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAoB,MAAM,uBAAuB,CAAC;AAG7F,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAEvD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,OAAO,CAAC;IACd;yEACqE;IACrE,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,yEAAyE;IACzE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,mEAAmE;IACnE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;mCAG+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAqCxC;6DAC6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAgH1D"}
|
package/dist/cli/args.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GATEWAY_USAGE, isGatewayCommand } from "./gateway-commands.js";
|
|
2
|
+
import { UPDATE_USAGE } from "./update-command.js";
|
|
2
3
|
export class UsageError extends Error {
|
|
3
4
|
}
|
|
4
5
|
const USAGE = `oma - Oma coding agent
|
|
@@ -11,6 +12,9 @@ Usage:
|
|
|
11
12
|
oma --mode json "<prompt>" json mode: all events + one outcome as JSONL
|
|
12
13
|
oma --mode rpc rpc mode: stdin/stdout JSONL protocol
|
|
13
14
|
oma --list-models print the model catalog as JSON
|
|
15
|
+
oma update update the CLI + gateway artifact to the
|
|
16
|
+
newest published version
|
|
17
|
+
oma update --check report what is installed vs what is published
|
|
14
18
|
oma gateway up start backend + web (foreground, Ctrl-C stops)
|
|
15
19
|
oma gateway up -d same, detached (returns once healthy)
|
|
16
20
|
oma gateway down stop a detached gateway
|
|
@@ -47,6 +51,10 @@ export function parseArgs(argv) {
|
|
|
47
51
|
// escape hatch when the word really is part of a prompt.
|
|
48
52
|
if (argv[0] === "gateway")
|
|
49
53
|
return parseGatewayArgs(argv.slice(1), args);
|
|
54
|
+
// Same reservation for `oma update` (the release updater): a bare `oma update`
|
|
55
|
+
// must not become the prompt "update".
|
|
56
|
+
if (argv[0] === "update")
|
|
57
|
+
return parseUpdateArgs(argv.slice(1), args);
|
|
50
58
|
let i = 0;
|
|
51
59
|
while (i < argv.length) {
|
|
52
60
|
const arg = argv[i];
|
|
@@ -163,6 +171,45 @@ export function parseArgs(argv) {
|
|
|
163
171
|
args.prompt = positional.join(" ");
|
|
164
172
|
return args;
|
|
165
173
|
}
|
|
174
|
+
/** Parse `oma update [--check] [--version <v>]`. Like the gateway surface it
|
|
175
|
+
* takes no prompt, session or mode — the first position is a verb. */
|
|
176
|
+
function parseUpdateArgs(rest, args) {
|
|
177
|
+
const first = rest[0];
|
|
178
|
+
if (first !== undefined && !first.startsWith("-")) {
|
|
179
|
+
throw new UsageError(`oma update takes no argument: ${first}\n\n${UPDATE_USAGE}`);
|
|
180
|
+
}
|
|
181
|
+
args.update = true;
|
|
182
|
+
let i = 0;
|
|
183
|
+
while (i < rest.length) {
|
|
184
|
+
const arg = rest[i] ?? "";
|
|
185
|
+
if (arg === "--check" || arg === "-c") {
|
|
186
|
+
args.updateCheck = true;
|
|
187
|
+
i += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (arg === "--version") {
|
|
191
|
+
const value = rest[i + 1];
|
|
192
|
+
if (!value || value.startsWith("-")) {
|
|
193
|
+
throw new UsageError("oma update --version needs a version like 0.2.0");
|
|
194
|
+
}
|
|
195
|
+
args.updateVersion = value;
|
|
196
|
+
i += 2;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (arg.startsWith("--version=")) {
|
|
200
|
+
const value = arg.slice("--version=".length);
|
|
201
|
+
if (!value)
|
|
202
|
+
throw new UsageError("oma update --version needs a version like 0.2.0");
|
|
203
|
+
args.updateVersion = value;
|
|
204
|
+
i += 1;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (arg === "--help" || arg === "-h")
|
|
208
|
+
throw new UsageError(UPDATE_USAGE);
|
|
209
|
+
throw new UsageError(`unknown update option: ${arg}\n\n${UPDATE_USAGE}`);
|
|
210
|
+
}
|
|
211
|
+
return args;
|
|
212
|
+
}
|
|
166
213
|
/** Parse `oma gateway <verb> [--version <v>]`. Kept separate from the main
|
|
167
214
|
* loop: the gateway surface takes no prompt, session or mode. */
|
|
168
215
|
function parseGatewayArgs(rest, args) {
|
|
@@ -17,6 +17,10 @@ export interface GatewayCommandOptions {
|
|
|
17
17
|
quiet?: boolean;
|
|
18
18
|
log?: (line: string) => void;
|
|
19
19
|
}
|
|
20
|
+
/** A source checkout runs the gateway from the repo, so the release artifact it
|
|
21
|
+
* would download is unused. Only stay quiet there: an explicit CLI call still
|
|
22
|
+
* fetches. */
|
|
23
|
+
export declare function inMonorepoCheckout(): boolean;
|
|
20
24
|
/** `oma gateway fetch`: download, verify and unpack the artifact for one
|
|
21
25
|
* version (default: oma's own) so `up` can start it. Idempotent. */
|
|
22
26
|
export declare function runGatewayFetch(opts?: GatewayCommandOptions): Promise<number>;
|
|
@@ -35,6 +39,14 @@ export declare function runGatewayPasswd(opts?: GatewayCommandOptions): Promise<
|
|
|
35
39
|
export declare function runGatewayDoctor(opts?: GatewayCommandOptions): Promise<number>;
|
|
36
40
|
/** `oma gateway down`: stop whatever `up -d` started. */
|
|
37
41
|
export declare function runGatewayDown(opts?: GatewayCommandOptions): Promise<number>;
|
|
42
|
+
/** Relaunch this CLI in the background and wait until it answers, so the command
|
|
43
|
+
* reports the truth instead of "started something, maybe". Also used by
|
|
44
|
+
* `oma update` to put a running gateway onto the version it just installed. */
|
|
45
|
+
export declare function startGatewayDetached(opts: GatewayCommandOptions & {
|
|
46
|
+
home: string;
|
|
47
|
+
version: string;
|
|
48
|
+
log: (line: string) => void;
|
|
49
|
+
}): Promise<number>;
|
|
38
50
|
/** `oma gateway <verb>`: single entry point so the CLI owns one code path. */
|
|
39
51
|
export declare function runGatewayCommand(verb: GatewayCommand, opts?: GatewayCommandOptions): Promise<number>;
|
|
40
52
|
//# sourceMappingURL=gateway-commands.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-commands.d.ts","sourceRoot":"","sources":["../../src/cli/gateway-commands.ts"],"names":[],"mappings":"AAwBA;iEACiE;AACjE,eAAO,MAAM,aAAa,gEAAiE,CAAC;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa,0pBAaF,CAAC;AAEzB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAKvE;AAED;;;uDAGuD;AACvD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,EAAE,OAAO,GACnB,MAAM,EAAE,CAMV;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"gateway-commands.d.ts","sourceRoot":"","sources":["../../src/cli/gateway-commands.ts"],"names":[],"mappings":"AAwBA;iEACiE;AACjE,eAAO,MAAM,aAAa,gEAAiE,CAAC;AAC5F,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,aAAa,0pBAaF,CAAC;AAEzB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAKvE;AAED;;;uDAGuD;AACvD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC/B,WAAW,EAAE,OAAO,GACnB,MAAM,EAAE,CAMV;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED;;eAEe;AACf,wBAAgB,kBAAkB,IAAI,OAAO,CAK5C;AAoBD;qEACqE;AACrE,wBAAsB,eAAe,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBvF;AAED;qEACqE;AACrE,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAgExF;AAED;iFACiF;AACjF,wBAAsB,YAAY,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCpF;AAED;;;mBAGmB;AACnB,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BxF;AAmDD,mFAAmF;AACnF,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBxF;AAED,yDAAyD;AACzD,wBAAsB,cAAc,CAAC,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAUtF;AAED;;gFAEgF;AAChF,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,qBAAqB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,GAC3F,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED,8EAA8E;AAC9E,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,qBAA0B,GAC/B,OAAO,CAAC,MAAM,CAAC,CAOjB"}
|
|
@@ -44,15 +44,19 @@ export function credentialLines(home, secrets, interactive) {
|
|
|
44
44
|
}
|
|
45
45
|
/** A source checkout runs the gateway from the repo, so the release artifact it
|
|
46
46
|
* would download is unused. Only stay quiet there: an explicit CLI call still
|
|
47
|
-
* fetches.
|
|
48
|
-
function
|
|
49
|
-
if (process.env.npm_lifecycle_event !== "postinstall")
|
|
50
|
-
return false;
|
|
47
|
+
* fetches. */
|
|
48
|
+
export function inMonorepoCheckout() {
|
|
51
49
|
// src/cli (dist/cli) is TWO levels under the package root — unlike
|
|
52
50
|
// core/gateway/*, which sits three. Getting this wrong made the guard a no-op.
|
|
53
51
|
const packageRoot = resolve(import.meta.dirname, "../..");
|
|
54
52
|
return existsSync(join(packageRoot, "..", "..", "turbo.json"));
|
|
55
53
|
}
|
|
54
|
+
/** `npm_lifecycle_event` is set while a package's postinstall runs. */
|
|
55
|
+
function inMonorepoCheckoutPostinstall() {
|
|
56
|
+
if (process.env.npm_lifecycle_event !== "postinstall")
|
|
57
|
+
return false;
|
|
58
|
+
return inMonorepoCheckout();
|
|
59
|
+
}
|
|
56
60
|
function loggerFor(opts, stream) {
|
|
57
61
|
if (opts.log)
|
|
58
62
|
return opts.log;
|
|
@@ -299,8 +303,9 @@ export async function runGatewayDown(opts = {}) {
|
|
|
299
303
|
return result.reason?.startsWith("stale pidfile") ? 0 : 1;
|
|
300
304
|
}
|
|
301
305
|
/** Relaunch this CLI in the background and wait until it answers, so the command
|
|
302
|
-
* reports the truth instead of "started something, maybe".
|
|
303
|
-
|
|
306
|
+
* reports the truth instead of "started something, maybe". Also used by
|
|
307
|
+
* `oma update` to put a running gateway onto the version it just installed. */
|
|
308
|
+
export async function startGatewayDetached(opts) {
|
|
304
309
|
const entry = process.argv[1];
|
|
305
310
|
if (!entry) {
|
|
306
311
|
opts.log("cannot detach: no CLI entry path in argv");
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type LatestRelease } from "../core/update/release.js";
|
|
2
|
+
/** Who owns the running oma, i.e. which command updates it. `oma update` only
|
|
3
|
+
* ever runs the installer it can identify: an unknown install (a hand-copied
|
|
4
|
+
* binary, another package manager, a distro package) gets the command printed
|
|
5
|
+
* instead of a guess that could fight the real owner. */
|
|
6
|
+
export type InstallOwner = {
|
|
7
|
+
kind: "bun-global";
|
|
8
|
+
spec: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "source";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "unknown";
|
|
13
|
+
};
|
|
14
|
+
/** bun's global install root: `<BUN_INSTALL|~/.bun>/install/global/node_modules`.
|
|
15
|
+
*
|
|
16
|
+
* `bun add -g` puts the package there and symlinks `bin/<name>` at it, so the
|
|
17
|
+
* running entry's real path is the test: an entry inside this tree is ours to
|
|
18
|
+
* reinstall, an entry anywhere else (project node_modules, a checkout) is not.
|
|
19
|
+
* Same tree the install script documents, so the two agree. */
|
|
20
|
+
export declare function bunGlobalRoot(env?: NodeJS.ProcessEnv): string;
|
|
21
|
+
/** Classify the install. Pure: every ambient fact (the entry path, whether this
|
|
22
|
+
* build lives in a checkout, the env) is passed in, so callers own detection
|
|
23
|
+
* and tests are not at the mercy of where the suite runs. */
|
|
24
|
+
export declare function detectInstallOwner(opts: {
|
|
25
|
+
entry?: string | undefined;
|
|
26
|
+
/** True when this build lives in a source checkout — either executed from
|
|
27
|
+
* src, or a dist that still sits inside the repo. */
|
|
28
|
+
fromCheckout: boolean;
|
|
29
|
+
env?: NodeJS.ProcessEnv;
|
|
30
|
+
version: string;
|
|
31
|
+
}): InstallOwner;
|
|
32
|
+
export interface UpdateCommandOptions {
|
|
33
|
+
home?: string;
|
|
34
|
+
/** Pin the target version instead of asking the registry. */
|
|
35
|
+
version?: string;
|
|
36
|
+
/** Report state and stop; no download, no install, no restart. */
|
|
37
|
+
check?: boolean;
|
|
38
|
+
log?: (line: string) => void;
|
|
39
|
+
quiet?: boolean;
|
|
40
|
+
/** Test seam: replaces the registry lookup. */
|
|
41
|
+
fetchLatest?: () => Promise<LatestRelease>;
|
|
42
|
+
/** Test seam: replaces `bun add -g` (returns its exit code). */
|
|
43
|
+
runInstall?: (argv: readonly string[]) => Promise<number>;
|
|
44
|
+
}
|
|
45
|
+
export interface UpdateState {
|
|
46
|
+
cliVersion: string;
|
|
47
|
+
artifactVersion?: string;
|
|
48
|
+
target: LatestRelease;
|
|
49
|
+
}
|
|
50
|
+
/** `oma update`: bring the CLI and the gateway artifact to the newest published
|
|
51
|
+
* version, and restart a detached gateway so it is not left on the old code.
|
|
52
|
+
*
|
|
53
|
+
* Refuses to downgrade any axis unless `--version` pins it on purpose. */
|
|
54
|
+
export declare function runUpdateCommand(opts?: UpdateCommandOptions): Promise<number>;
|
|
55
|
+
/** `oma update` help text (the args module owns verb discovery). */
|
|
56
|
+
export declare const UPDATE_USAGE = "oma update [--check] [--version <v>]\n\n (no flags) install the newest published CLI + gateway artifact, and\n restart a detached gateway onto the new version\n --check report what is installed vs what is published; exits 1 when\n an update is available, 0 when up to date\n --version <v> pin a version instead of asking the registry (also allows\n reinstalling a version older than the local one)\n\nNote: 'update' is a reserved first word. To send it as a prompt, use\n'oma -p \"update ...\"'.";
|
|
57
|
+
//# sourceMappingURL=update-command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-command.d.ts","sourceRoot":"","sources":["../../src/cli/update-command.ts"],"names":[],"mappings":"AAYA,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,2BAA2B,CAAC;AAGnC;;;0DAG0D;AAC1D,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB;;;;;gEAKgE;AAChE,wBAAgB,aAAa,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAG1E;AAED;;8DAE8D;AAC9D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACvC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;0DACsD;IACtD,YAAY,EAAE,OAAO,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,YAAY,CAkBf;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3C,gEAAgE;IAChE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC;CACvB;AAwCD;;;2EAG2E;AAC3E,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8FvF;AA6CD,oEAAoE;AACpE,eAAO,MAAM,YAAY,yjBAUF,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, sep } from "node:path";
|
|
4
|
+
import { currentVersion, fetchGatewayArtifact, gatewayPaths, omaHome, omaVersion, } from "../core/gateway/artifact.js";
|
|
5
|
+
import { isProcessAlive, readDaemon, stopDetachedGateway } from "../core/gateway/daemon.js";
|
|
6
|
+
import { fetchLatestRelease, isNewer, OMA_PACKAGE, } from "../core/update/release.js";
|
|
7
|
+
import { inMonorepoCheckout, startGatewayDetached } from "./gateway-commands.js";
|
|
8
|
+
/** bun's global install root: `<BUN_INSTALL|~/.bun>/install/global/node_modules`.
|
|
9
|
+
*
|
|
10
|
+
* `bun add -g` puts the package there and symlinks `bin/<name>` at it, so the
|
|
11
|
+
* running entry's real path is the test: an entry inside this tree is ours to
|
|
12
|
+
* reinstall, an entry anywhere else (project node_modules, a checkout) is not.
|
|
13
|
+
* Same tree the install script documents, so the two agree. */
|
|
14
|
+
export function bunGlobalRoot(env = process.env) {
|
|
15
|
+
const install = env.BUN_INSTALL ?? join(homedir(), ".bun");
|
|
16
|
+
return join(install, "install", "global", "node_modules");
|
|
17
|
+
}
|
|
18
|
+
/** Classify the install. Pure: every ambient fact (the entry path, whether this
|
|
19
|
+
* build lives in a checkout, the env) is passed in, so callers own detection
|
|
20
|
+
* and tests are not at the mercy of where the suite runs. */
|
|
21
|
+
export function detectInstallOwner(opts) {
|
|
22
|
+
const spec = `${OMA_PACKAGE}@${opts.version}`;
|
|
23
|
+
// A checkout is not updated by reinstalling the package: telling a developer
|
|
24
|
+
// to `bun add -g` the package they are editing would fight their working tree.
|
|
25
|
+
if (opts.fromCheckout)
|
|
26
|
+
return { kind: "source" };
|
|
27
|
+
if (!opts.entry)
|
|
28
|
+
return { kind: "unknown" };
|
|
29
|
+
let real;
|
|
30
|
+
let root;
|
|
31
|
+
try {
|
|
32
|
+
real = realpathSync(opts.entry);
|
|
33
|
+
root = realpathSync(bunGlobalRoot(opts.env ?? process.env));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return { kind: "unknown" };
|
|
37
|
+
}
|
|
38
|
+
if (real.startsWith(root.endsWith(sep) ? root : `${root}${sep}`)) {
|
|
39
|
+
return { kind: "bun-global", spec };
|
|
40
|
+
}
|
|
41
|
+
return { kind: "unknown" };
|
|
42
|
+
}
|
|
43
|
+
/** What is installed right now, next to what the registry offers. Shared by
|
|
44
|
+
* `--check` and the real update so the two reports cannot drift. */
|
|
45
|
+
async function resolveState(opts) {
|
|
46
|
+
const home = opts.home ?? omaHome();
|
|
47
|
+
const target = opts.version
|
|
48
|
+
? { version: opts.version, tag: opts.version }
|
|
49
|
+
: await (opts.fetchLatest ?? (() => fetchLatestRelease()))();
|
|
50
|
+
return { cliVersion: omaVersion(), artifactVersion: currentVersion(home), target };
|
|
51
|
+
}
|
|
52
|
+
/** Print the two axes separately: the CLI (npm package) and the gateway
|
|
53
|
+
* artifact (the backend + web tarball) are installed by different steps and
|
|
54
|
+
* routinely disagree — an up-to-date CLI next to a stale artifact is exactly
|
|
55
|
+
* the state that produced a gateway which could not start. */
|
|
56
|
+
function reportLines(state, home) {
|
|
57
|
+
const { cliVersion, artifactVersion, target } = state;
|
|
58
|
+
const lines = [];
|
|
59
|
+
const cliNote = isNewer(target.version, cliVersion)
|
|
60
|
+
? "update available"
|
|
61
|
+
: cliVersion === target.version
|
|
62
|
+
? "current"
|
|
63
|
+
: "newer than the registry (a local build?)";
|
|
64
|
+
lines.push(`oma CLI: ${cliVersion} (${cliNote})`);
|
|
65
|
+
lines.push(artifactVersion === undefined
|
|
66
|
+
? `gateway artifact: none installed (${gatewayPaths(home).versions})`
|
|
67
|
+
: `gateway artifact: ${artifactVersion}${artifactVersion === target.version
|
|
68
|
+
? " (current)"
|
|
69
|
+
: isNewer(target.version, artifactVersion)
|
|
70
|
+
? " (update available)"
|
|
71
|
+
: " (newer than the registry)"}`);
|
|
72
|
+
lines.push(`newest published: ${target.version} (dist-tag ${target.tag})`);
|
|
73
|
+
return lines;
|
|
74
|
+
}
|
|
75
|
+
/** `oma update`: bring the CLI and the gateway artifact to the newest published
|
|
76
|
+
* version, and restart a detached gateway so it is not left on the old code.
|
|
77
|
+
*
|
|
78
|
+
* Refuses to downgrade any axis unless `--version` pins it on purpose. */
|
|
79
|
+
export async function runUpdateCommand(opts = {}) {
|
|
80
|
+
const home = opts.home ?? omaHome();
|
|
81
|
+
const log = loggerFor(opts);
|
|
82
|
+
let state;
|
|
83
|
+
try {
|
|
84
|
+
state = await resolveState(opts);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
log(`oma update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
const { cliVersion, artifactVersion, target } = state;
|
|
91
|
+
if (opts.check) {
|
|
92
|
+
log(`oma update --check (home: ${home})`);
|
|
93
|
+
for (const line of reportLines(state, home))
|
|
94
|
+
log(line);
|
|
95
|
+
const behind = isNewer(target.version, cliVersion) || isNewer(target.version, artifactVersion ?? "0.0.0");
|
|
96
|
+
if (!behind) {
|
|
97
|
+
log("");
|
|
98
|
+
log("up to date");
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
log("");
|
|
102
|
+
log("run `oma update` to install it");
|
|
103
|
+
return 1;
|
|
104
|
+
}
|
|
105
|
+
// No silent downgrades. The registry is the newest *published* thing, not
|
|
106
|
+
// necessarily the newest thing this machine has: a checkout of the same
|
|
107
|
+
// version, or a locally built one, must not be replaced by an older release.
|
|
108
|
+
const pinned = opts.version !== undefined;
|
|
109
|
+
if (!pinned && isNewer(cliVersion, target.version)) {
|
|
110
|
+
log(`the CLI (${cliVersion}) is newer than the newest published version (${target.version}); nothing to do (pin with --version to force)`);
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
log(`oma update: ${cliVersion} -> ${target.version} (dist-tag ${target.tag})`);
|
|
114
|
+
// 1. The artifact first: it is the half that can be broken while the CLI is
|
|
115
|
+
// fine, and it is what `oma gateway up` needs. A failure here is fatal.
|
|
116
|
+
if (artifactVersion === target.version) {
|
|
117
|
+
log(`gateway artifact ${target.version} is already installed`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
try {
|
|
121
|
+
const result = await fetchGatewayArtifact({ home, version: target.version, log });
|
|
122
|
+
log(result.downloaded
|
|
123
|
+
? `gateway artifact ${target.version} installed`
|
|
124
|
+
: `gateway artifact ${target.version} was already unpacked`);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
log(`gateway artifact update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
128
|
+
return 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 2. The CLI itself. Not fatal: the artifact above is already in place, and
|
|
132
|
+
// an install that cannot be identified gets instructions instead of a
|
|
133
|
+
// guess (reinstalling through the wrong owner silently pins the version).
|
|
134
|
+
const owner = detectInstallOwner({
|
|
135
|
+
entry: process.argv[1],
|
|
136
|
+
// Two spellings of "a checkout": executed from src (.ts), or a built dist
|
|
137
|
+
// that still lives inside the repo.
|
|
138
|
+
fromCheckout: import.meta.filename.endsWith(".ts") || inMonorepoCheckout(),
|
|
139
|
+
version: target.version,
|
|
140
|
+
});
|
|
141
|
+
if (!isNewer(target.version, cliVersion)) {
|
|
142
|
+
log(`oma CLI ${cliVersion} is current`);
|
|
143
|
+
}
|
|
144
|
+
else if (owner.kind === "bun-global") {
|
|
145
|
+
const code = await (opts.runInstall ?? runBunInstall)([
|
|
146
|
+
process.execPath,
|
|
147
|
+
"add",
|
|
148
|
+
"-g",
|
|
149
|
+
owner.spec,
|
|
150
|
+
]);
|
|
151
|
+
log(code === 0 ? `oma CLI updated to ${target.version}` : `oma CLI install exited ${code}`);
|
|
152
|
+
if (code !== 0)
|
|
153
|
+
log(`install it by hand: bun add -g ${owner.spec}`);
|
|
154
|
+
}
|
|
155
|
+
else if (owner.kind === "source") {
|
|
156
|
+
log("running from a checkout: update the code with git pull (the artifact above is current)");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
log("cannot tell how this oma was installed; update it with:");
|
|
160
|
+
log(` bun add -g ${OMA_PACKAGE}@${target.version}`);
|
|
161
|
+
}
|
|
162
|
+
// 3. A detached gateway is still running the old artifact. Restart it onto
|
|
163
|
+
// the version just installed; a foreground one is the user's to restart.
|
|
164
|
+
await restartRunningGateway({ ...opts, home, version: target.version, log });
|
|
165
|
+
log("");
|
|
166
|
+
log(`done. start it with: oma gateway up`);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
/** Restart the detached gateway when it is alive AND on a different version.
|
|
170
|
+
* Never touches a foreground run (it has no pidfile — we do not own it). */
|
|
171
|
+
async function restartRunningGateway(opts) {
|
|
172
|
+
const daemon = readDaemon(opts.home);
|
|
173
|
+
if (!daemon || !isProcessAlive(daemon.pid))
|
|
174
|
+
return;
|
|
175
|
+
if (daemon.version === opts.version) {
|
|
176
|
+
opts.log(`the running gateway is already on ${opts.version}`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
opts.log(`restarting the detached gateway (pid ${daemon.pid}, ${daemon.version} -> ${opts.version})`);
|
|
180
|
+
const stopped = await stopDetachedGateway(opts.home);
|
|
181
|
+
if (!stopped.stopped && !stopped.reason?.startsWith("stale pidfile")) {
|
|
182
|
+
opts.log(`could not stop the old gateway: ${stopped.reason ?? "unknown reason"}`);
|
|
183
|
+
opts.log("stop it yourself, then: oma gateway up -d");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const started = await startGatewayDetached({
|
|
187
|
+
home: opts.home,
|
|
188
|
+
version: opts.version,
|
|
189
|
+
log: opts.log,
|
|
190
|
+
});
|
|
191
|
+
if (started !== 0)
|
|
192
|
+
opts.log("the new version did not become healthy — check the log above");
|
|
193
|
+
}
|
|
194
|
+
/** `bun add -g`, inherited stdio so the user sees it. `process.execPath` is the
|
|
195
|
+
* bun running this CLI, so no PATH lookup (and no wrong-bun surprise). */
|
|
196
|
+
async function runBunInstall(argv) {
|
|
197
|
+
const proc = Bun.spawn([...argv], { stdin: "inherit", stdout: "inherit", stderr: "inherit" });
|
|
198
|
+
return await proc.exited;
|
|
199
|
+
}
|
|
200
|
+
function loggerFor(opts) {
|
|
201
|
+
if (opts.log)
|
|
202
|
+
return opts.log;
|
|
203
|
+
if (opts.quiet)
|
|
204
|
+
return () => { };
|
|
205
|
+
return (line) => {
|
|
206
|
+
process.stdout.write(`${line}\n`);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** `oma update` help text (the args module owns verb discovery). */
|
|
210
|
+
export const UPDATE_USAGE = `oma update [--check] [--version <v>]
|
|
211
|
+
|
|
212
|
+
(no flags) install the newest published CLI + gateway artifact, and
|
|
213
|
+
restart a detached gateway onto the new version
|
|
214
|
+
--check report what is installed vs what is published; exits 1 when
|
|
215
|
+
an update is available, 0 when up to date
|
|
216
|
+
--version <v> pin a version instead of asking the registry (also allows
|
|
217
|
+
reinstalling a version older than the local one)
|
|
218
|
+
|
|
219
|
+
Note: 'update' is a reserved first word. To send it as a prompt, use
|
|
220
|
+
'oma -p "update ..."'.`;
|
|
@@ -14,8 +14,26 @@ export interface DoctorOptions {
|
|
|
14
14
|
/** Seam for tests: replaces the release HEAD probe. Returns the HTTP status,
|
|
15
15
|
* or undefined when the host could not be reached. */
|
|
16
16
|
probeRelease?: (url: string) => Promise<number | undefined>;
|
|
17
|
+
/** Seam for tests: replaces the Lark probe (cli version + backend surfaces). */
|
|
18
|
+
probeLark?: () => Promise<LarkProbe>;
|
|
19
|
+
}
|
|
20
|
+
/** What the Lark diagnosis needs: is lark-cli here, and what do the surfaces
|
|
21
|
+
* the CONTROL PLANE owns say? The CLI reports; it does not become a second
|
|
22
|
+
* Lark console (no `oma lark *` verbs on purpose). */
|
|
23
|
+
export interface LarkProbe {
|
|
24
|
+
/** `lark-cli version X.Y.Z`, or null when the binary is not on PATH. */
|
|
25
|
+
cliVersion: string | null;
|
|
26
|
+
/** False when the backend did not answer: then there is nothing to report. */
|
|
27
|
+
reachable: boolean;
|
|
28
|
+
surfaces: Array<{
|
|
29
|
+
agentId: string;
|
|
30
|
+
agentName: string;
|
|
31
|
+
status: string;
|
|
32
|
+
lastError: string | null;
|
|
33
|
+
}>;
|
|
17
34
|
}
|
|
18
35
|
/** Diagnose why a gateway may not be able to run anything: artifact, tools,
|
|
19
36
|
* catalog source, runnable providers, ports, and the per-run oma binary. */
|
|
20
37
|
export declare function diagnoseGateway(opts?: DoctorOptions): Promise<GatewayCheck[]>;
|
|
38
|
+
export declare function defaultLarkProbe(home: string): () => Promise<LarkProbe>;
|
|
21
39
|
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/doctor.ts"],"names":[],"mappings":"AAeA,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD;2DACuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/core/gateway/doctor.ts"],"names":[],"mappings":"AAeA,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD;2DACuD;IACvD,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC5D,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,SAAS,CAAC,CAAC;CACtC;AAED;;uDAEuD;AACvD,MAAM,WAAW,SAAS;IACxB,wEAAwE;IACxE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,KAAK,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;KAC1B,CAAC,CAAC;CACJ;AAoED;6EAC6E;AAC7E,wBAAsB,eAAe,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAyXvF;AAgBD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,OAAO,CAAC,SAAS,CAAC,CA2CvE"}
|
|
@@ -388,7 +388,52 @@ export async function diagnoseGateway(opts = {}) {
|
|
|
388
388
|
// The backend answered /health but not this route (older artifact).
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
|
-
// 13. the
|
|
391
|
+
// 13. the Lark surface: is it runnable here, and is anything wrong? The
|
|
392
|
+
// state itself lives in the backend (agent config + registry + heartbeat);
|
|
393
|
+
// this only summarizes it and points at where to fix it.
|
|
394
|
+
const lark = await (opts.probeLark ?? defaultLarkProbe(home))();
|
|
395
|
+
checks.push({
|
|
396
|
+
id: "lark-cli",
|
|
397
|
+
ok: true,
|
|
398
|
+
detail: lark.cliVersion === null
|
|
399
|
+
? "lark-cli not found on PATH (no agent can be connected to Lark here)"
|
|
400
|
+
: `lark-cli ${lark.cliVersion}`,
|
|
401
|
+
...(lark.cliVersion === null
|
|
402
|
+
? { fix: "install lark-cli, then connect an agent from the web UI (agent -> Lark tab)" }
|
|
403
|
+
: {}),
|
|
404
|
+
});
|
|
405
|
+
if (!lark.reachable) {
|
|
406
|
+
checks.push({
|
|
407
|
+
id: "lark-surface",
|
|
408
|
+
ok: true,
|
|
409
|
+
detail: "no Lark state to report (the backend is not answering)",
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
else if (lark.surfaces.length === 0) {
|
|
413
|
+
checks.push({ id: "lark-surface", ok: true, detail: "no agent has a Lark surface" });
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
const running = lark.surfaces.filter((s) => s.status === "running");
|
|
417
|
+
const broken = lark.surfaces.filter((s) => s.status === "degraded" || s.status === "error");
|
|
418
|
+
checks.push({
|
|
419
|
+
id: "lark-surface",
|
|
420
|
+
ok: broken.length === 0,
|
|
421
|
+
detail: broken.length === 0
|
|
422
|
+
? `${lark.surfaces.length} configured, ${running.length} running`
|
|
423
|
+
: `${lark.surfaces.length} configured, ${running.length} running, ${broken.length} needing attention: ` +
|
|
424
|
+
broken
|
|
425
|
+
.map((s) => `${s.agentName} (${s.status}${s.lastError ? `: ${s.lastError}` : ""})`)
|
|
426
|
+
.join("; "),
|
|
427
|
+
...(broken.length > 0
|
|
428
|
+
? {
|
|
429
|
+
fix: broken
|
|
430
|
+
.map((s) => `open http://127.0.0.1:3001/team/${s.agentId}?tab=lark`)
|
|
431
|
+
.join(" | "),
|
|
432
|
+
}
|
|
433
|
+
: {}),
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
// 14. the binary the backend spawns per Run
|
|
392
437
|
try {
|
|
393
438
|
const entry = resolveOmaBin();
|
|
394
439
|
checks.push({ id: "oma-bin", ok: true, detail: `runs will spawn ${entry}` });
|
|
@@ -403,3 +448,66 @@ export async function diagnoseGateway(opts = {}) {
|
|
|
403
448
|
}
|
|
404
449
|
return checks;
|
|
405
450
|
}
|
|
451
|
+
/** `lark-cli version X.Y.Z` -> "X.Y.Z". Absent binary is reported, not fatal:
|
|
452
|
+
* Lark is optional, so a gateway without it still passes doctor. */
|
|
453
|
+
function probeLarkCliVersion() {
|
|
454
|
+
try {
|
|
455
|
+
const out = Bun.spawnSync(["lark-cli", "--version"], { stdout: "pipe", stderr: "pipe" });
|
|
456
|
+
if (out.exitCode !== 0)
|
|
457
|
+
return null;
|
|
458
|
+
const text = out.stdout.toString().trim();
|
|
459
|
+
const match = text.match(/\d+\.\d+(?:\.\d+)?/);
|
|
460
|
+
return match ? match[0] : text.slice(0, 24) || null;
|
|
461
|
+
}
|
|
462
|
+
catch {
|
|
463
|
+
return null;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
export function defaultLarkProbe(home) {
|
|
467
|
+
return async () => {
|
|
468
|
+
const cliVersion = probeLarkCliVersion();
|
|
469
|
+
try {
|
|
470
|
+
const token = readSecrets(home).BACKEND_AUTH_TOKEN ?? "";
|
|
471
|
+
const res = await fetch("http://127.0.0.1:3000/api/ops/surfaces", {
|
|
472
|
+
headers: { "x-auth-token": token },
|
|
473
|
+
signal: AbortSignal.timeout(2000),
|
|
474
|
+
});
|
|
475
|
+
// A 401/500 body is not "no surfaces configured": without a readable
|
|
476
|
+
// answer the doctor must say it could not look, not that nothing exists.
|
|
477
|
+
if (!res.ok)
|
|
478
|
+
return { cliVersion, reachable: false, surfaces: [] };
|
|
479
|
+
const body = await res.json();
|
|
480
|
+
// The route returns a bare array today; accept a wrapped shape too so a
|
|
481
|
+
// future envelope does not silently read as "no Lark surfaces".
|
|
482
|
+
const items = Array.isArray(body)
|
|
483
|
+
? body
|
|
484
|
+
: typeof body === "object" && body !== null && "surfaces" in body
|
|
485
|
+
? body.surfaces
|
|
486
|
+
: undefined;
|
|
487
|
+
if (!Array.isArray(items))
|
|
488
|
+
return { cliVersion, reachable: true, surfaces: [] };
|
|
489
|
+
const surfaces = [];
|
|
490
|
+
for (const item of items) {
|
|
491
|
+
if (typeof item !== "object" || item === null)
|
|
492
|
+
continue;
|
|
493
|
+
// The web chip matches on a substring (`lark-bot` and friends), so an
|
|
494
|
+
// exact "lark" comparison silently reported an empty deployment.
|
|
495
|
+
if (!("surface" in item) || typeof item.surface !== "string")
|
|
496
|
+
continue;
|
|
497
|
+
if (!item.surface.toLowerCase().includes("lark"))
|
|
498
|
+
continue;
|
|
499
|
+
const status = "status" in item && typeof item.status === "string" ? item.status : "unknown";
|
|
500
|
+
const agentId = "agentId" in item && typeof item.agentId === "string" ? item.agentId : "";
|
|
501
|
+
const agentName = "agentName" in item && typeof item.agentName === "string" ? item.agentName : agentId;
|
|
502
|
+
const lastError = "lastError" in item && typeof item.lastError === "string" ? item.lastError : null;
|
|
503
|
+
if (agentId === "")
|
|
504
|
+
continue;
|
|
505
|
+
surfaces.push({ agentId, agentName, status, lastError });
|
|
506
|
+
}
|
|
507
|
+
return { cliVersion, reachable: true, surfaces };
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return { cliVersion, reachable: false, surfaces: [] };
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
}
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
import type { ToolPresentation } from "@chengchenccc/agent-contract";
|
|
1
2
|
import type { TodoItem } from "../tools/todo-store.js";
|
|
2
3
|
/** Pi-style typed lifecycle events per runtime/oma.md. */
|
|
3
4
|
export type OmaLoopEvent = {
|
|
4
5
|
type: "agent_start";
|
|
6
|
+
}
|
|
7
|
+
/** Liveness only: proves the loop is alive while it waits on something that
|
|
8
|
+
* produces no events (a long tool, a slow model call, a subagent). Carries
|
|
9
|
+
* no content, is never persisted, and every surface ignores it. */
|
|
10
|
+
| {
|
|
11
|
+
type: "heartbeat";
|
|
5
12
|
} | {
|
|
6
13
|
type: "agent_end";
|
|
7
14
|
status: "completed" | "failed" | "stopped";
|
|
@@ -26,8 +33,17 @@ export type OmaLoopEvent = {
|
|
|
26
33
|
toolName: string;
|
|
27
34
|
kind?: "native" | "product";
|
|
28
35
|
callId: string;
|
|
29
|
-
/** The tool's resolved input (model call args), for transcript display.
|
|
36
|
+
/** The tool's resolved input (model call args), for transcript display.
|
|
37
|
+
* oma-internal: this NEVER crosses the RPC boundary — surfaces get
|
|
38
|
+
* `activity` instead. */
|
|
30
39
|
input?: Readonly<Record<string, unknown>>;
|
|
40
|
+
/** User-visible one-line description of what the call is doing,
|
|
41
|
+
* produced by the tool's describeStart and sanitized by
|
|
42
|
+
* safeToolSummary. Absent means "the tool cannot describe itself" —
|
|
43
|
+
* surfaces then fall back to the tool name. */
|
|
44
|
+
activity?: string;
|
|
45
|
+
/** Structured form of the same description (title/detail/icon). */
|
|
46
|
+
presentation?: ToolPresentation;
|
|
31
47
|
/** Wall-clock timeout for this tool (ms, 0 = disabled). */
|
|
32
48
|
timeoutMs?: number;
|
|
33
49
|
} | {
|
|
@@ -36,6 +52,8 @@ export type OmaLoopEvent = {
|
|
|
36
52
|
kind?: "native" | "product";
|
|
37
53
|
callId: string;
|
|
38
54
|
result?: Readonly<Record<string, unknown>>;
|
|
55
|
+
/** Result-side display metadata (resultSummary / errorSummary). */
|
|
56
|
+
presentation?: ToolPresentation;
|
|
39
57
|
} | {
|
|
40
58
|
/** Streaming partial output from a running tool (bash stdout). */
|
|
41
59
|
type: "tool_output";
|