omkit 0.4.0 → 0.4.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/dist/cli/client/discovery.d.ts +3 -2
- package/dist/cli/client/discovery.js +27 -11
- package/dist/cli/commands/ui.d.ts +5 -1
- package/dist/cli/commands/ui.js +9 -2
- package/dist/cli/index.js +22 -3
- package/dist/cli/ui/app.d.ts +4 -1
- package/dist/cli/ui/app.js +5 -3
- package/dist/core/ActionRun.js +7 -2
- package/package.json +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Registry } from "./registry.ts";
|
|
2
2
|
/**
|
|
3
3
|
* Statically scan the project described by `tsconfigPath` for runnable oms and inspectable
|
|
4
|
-
* actions.
|
|
5
|
-
*
|
|
4
|
+
* actions. Type errors in the user's oms/actions degrade to `warnings` (an editor-in-progress
|
|
5
|
+
* project stays useful), but a **fatal config problem** — a missing, unparseable, or invalid
|
|
6
|
+
* tsconfig — throws, so the CLI crashes visibly instead of silently returning an empty project.
|
|
6
7
|
*/
|
|
7
8
|
export declare function discover(tsconfigPath: string): Registry;
|
|
@@ -2,22 +2,16 @@ import ts from "typescript";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
/**
|
|
4
4
|
* Statically scan the project described by `tsconfigPath` for runnable oms and inspectable
|
|
5
|
-
* actions.
|
|
6
|
-
*
|
|
5
|
+
* actions. Type errors in the user's oms/actions degrade to `warnings` (an editor-in-progress
|
|
6
|
+
* project stays useful), but a **fatal config problem** — a missing, unparseable, or invalid
|
|
7
|
+
* tsconfig — throws, so the CLI crashes visibly instead of silently returning an empty project.
|
|
7
8
|
*/
|
|
8
9
|
export function discover(tsconfigPath) {
|
|
9
10
|
const oms = [];
|
|
10
11
|
const actions = [];
|
|
11
12
|
const warnings = [];
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
warnings.push(ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n"));
|
|
15
|
-
return { oms, actions, warnings };
|
|
16
|
-
}
|
|
17
|
-
// Resolve to an absolute base so `parsed.fileNames` are absolute. Otherwise root files stay
|
|
18
|
-
// relative while imported files (e.g. an action pulled in by an om) resolve to absolute paths,
|
|
19
|
-
// and the two never match in `fileSet`.
|
|
20
|
-
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.resolve(path.dirname(tsconfigPath)));
|
|
13
|
+
const { parsed, warnings: configWarnings } = loadProject(tsconfigPath);
|
|
14
|
+
warnings.push(...configWarnings);
|
|
21
15
|
const program = ts.createProgram({
|
|
22
16
|
rootNames: parsed.fileNames,
|
|
23
17
|
options: { ...parsed.options, noEmit: true },
|
|
@@ -62,6 +56,28 @@ export function discover(tsconfigPath) {
|
|
|
62
56
|
}
|
|
63
57
|
return { oms, actions, warnings };
|
|
64
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Load and validate the tsconfig, throwing on a fatal config problem — a missing/unparseable file,
|
|
61
|
+
* or a config-level error (bad compilerOptions, an unresolvable `extends`). The benign "no inputs
|
|
62
|
+
* found" (TS18003) is not fatal: an empty-but-valid project comes back as a warning instead.
|
|
63
|
+
*
|
|
64
|
+
* Parses against an absolute base dir so `parsed.fileNames` are absolute — otherwise root files
|
|
65
|
+
* stay relative while imported files (an action pulled in by an om) resolve absolute, and the two
|
|
66
|
+
* never match in the caller's `fileSet`.
|
|
67
|
+
*/
|
|
68
|
+
function loadProject(tsconfigPath) {
|
|
69
|
+
const flatten = (d) => ts.flattenDiagnosticMessageText(d.messageText, "\n");
|
|
70
|
+
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
71
|
+
if (configFile.error)
|
|
72
|
+
throw new Error(`could not read ${tsconfigPath}: ${flatten(configFile.error)}`);
|
|
73
|
+
const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.resolve(path.dirname(tsconfigPath)));
|
|
74
|
+
const fatal = parsed.errors.filter((e) => e.category === ts.DiagnosticCategory.Error && e.code !== 18003);
|
|
75
|
+
if (fatal.length) {
|
|
76
|
+
throw new Error(`invalid ${tsconfigPath}:\n${fatal.map((e) => ` ${flatten(e)}`).join("\n")}`);
|
|
77
|
+
}
|
|
78
|
+
const warnings = parsed.errors.filter((e) => e.code === 18003).map(flatten);
|
|
79
|
+
return { parsed, warnings };
|
|
80
|
+
}
|
|
65
81
|
/** Collect the local names bound to om/step/action from `import … from "omkit"`. */
|
|
66
82
|
function omkitImports(sf) {
|
|
67
83
|
const names = {};
|
|
@@ -3,5 +3,9 @@ import type { OmkitClient } from "../client/index.ts";
|
|
|
3
3
|
* Render the interactive Ink app. Ink's own Ctrl+C handling is disabled so the app can tear the
|
|
4
4
|
* run down gracefully first; the final verdict + run-folder path are printed here, after Ink
|
|
5
5
|
* unmounts (it erases its own frame), so the pointer to the on-disk record survives on screen.
|
|
6
|
+
*
|
|
7
|
+
* Discovery runs first, before Ink takes the terminal: a fatal config problem throws here and the
|
|
8
|
+
* bin's error boundary turns it into a clean crash rather than an empty list. Non-fatal warnings
|
|
9
|
+
* are printed so they aren't lost behind the UI.
|
|
6
10
|
*/
|
|
7
|
-
export declare function launchUi(client: OmkitClient): void
|
|
11
|
+
export declare function launchUi(client: OmkitClient): Promise<void>;
|
package/dist/cli/commands/ui.js
CHANGED
|
@@ -5,10 +5,17 @@ import { App } from "../ui/app.js";
|
|
|
5
5
|
* Render the interactive Ink app. Ink's own Ctrl+C handling is disabled so the app can tear the
|
|
6
6
|
* run down gracefully first; the final verdict + run-folder path are printed here, after Ink
|
|
7
7
|
* unmounts (it erases its own frame), so the pointer to the on-disk record survives on screen.
|
|
8
|
+
*
|
|
9
|
+
* Discovery runs first, before Ink takes the terminal: a fatal config problem throws here and the
|
|
10
|
+
* bin's error boundary turns it into a clean crash rather than an empty list. Non-fatal warnings
|
|
11
|
+
* are printed so they aren't lost behind the UI.
|
|
8
12
|
*/
|
|
9
|
-
export function launchUi(client) {
|
|
13
|
+
export async function launchUi(client) {
|
|
14
|
+
const registry = await client.discover();
|
|
15
|
+
for (const w of registry.warnings)
|
|
16
|
+
process.stderr.write(`omkit: warning: ${w}\n`);
|
|
10
17
|
let finalVerdict;
|
|
11
|
-
const app = render(_jsx(App, { client: client, onExit: (v) => (finalVerdict = v) }), {
|
|
18
|
+
const app = render(_jsx(App, { client: client, oms: registry.oms, onExit: (v) => (finalVerdict = v) }), {
|
|
12
19
|
exitOnCtrlC: false,
|
|
13
20
|
});
|
|
14
21
|
void app.waitUntilExit().then(() => {
|
package/dist/cli/index.js
CHANGED
|
@@ -51,7 +51,7 @@ async function main() {
|
|
|
51
51
|
// A bare `omkit` lands here too, since `run` is the default command.
|
|
52
52
|
if (!cli.target) {
|
|
53
53
|
const { launchUi } = await import("./commands/ui.js");
|
|
54
|
-
launchUi(client);
|
|
54
|
+
await launchUi(client);
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
57
|
const { resolveOm } = await import("./commands/run.js");
|
|
@@ -87,5 +87,24 @@ function isEntryPoint() {
|
|
|
87
87
|
return false;
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
/** Print a fatal error to stderr in one consistent shape, just before the process exits non-zero. */
|
|
91
|
+
function reportFatal(err) {
|
|
92
|
+
const detail = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
93
|
+
process.stderr.write(`\nomkit: ${detail}\n`);
|
|
94
|
+
}
|
|
95
|
+
if (isEntryPoint()) {
|
|
96
|
+
// Nothing should fail silently. A fatal anywhere — a bad config, an uncaught throw, or a rejected
|
|
97
|
+
// promise in the interactive app — prints and crashes the CLI with a non-zero exit code.
|
|
98
|
+
process.on("uncaughtException", (err) => {
|
|
99
|
+
reportFatal(err);
|
|
100
|
+
process.exit(1);
|
|
101
|
+
});
|
|
102
|
+
process.on("unhandledRejection", (err) => {
|
|
103
|
+
reportFatal(err);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
});
|
|
106
|
+
main().catch((err) => {
|
|
107
|
+
reportFatal(err);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
});
|
|
110
|
+
}
|
package/dist/cli/ui/app.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { type ReactElement } from "react";
|
|
2
2
|
import type { OmkitClient, Verdict } from "../client/types.ts";
|
|
3
|
+
import type { DiscoveredOm } from "../client/registry.ts";
|
|
3
4
|
/** The interactive app: discover → list/search → run → live milestones + prompts. */
|
|
4
|
-
export declare function App({ client, onExit, }: {
|
|
5
|
+
export declare function App({ client, onExit, oms: initialOms, }: {
|
|
5
6
|
client: OmkitClient;
|
|
6
7
|
/** Called with the run's verdict (if any) just before Ink unmounts, so the caller can print
|
|
7
8
|
* a durable summary — Ink erases its own frame on exit. */
|
|
8
9
|
onExit?: (verdict: Verdict | undefined) => void;
|
|
10
|
+
/** Oms discovered by the caller (pre-flight). When given, the app skips its own discovery. */
|
|
11
|
+
oms?: DiscoveredOm[];
|
|
9
12
|
}): ReactElement;
|
package/dist/cli/ui/app.js
CHANGED
|
@@ -6,9 +6,9 @@ import { OmList } from "./views/OmList.js";
|
|
|
6
6
|
import { RunView } from "./views/RunView.js";
|
|
7
7
|
import { Spinner } from "./Spinner.js";
|
|
8
8
|
/** The interactive app: discover → list/search → run → live milestones + prompts. */
|
|
9
|
-
export function App({ client, onExit, }) {
|
|
9
|
+
export function App({ client, onExit, oms: initialOms, }) {
|
|
10
10
|
const { exit } = useApp();
|
|
11
|
-
const [oms, setOms] = useState([]);
|
|
11
|
+
const [oms, setOms] = useState(initialOms ?? []);
|
|
12
12
|
const [session, setSession] = useState(null);
|
|
13
13
|
const [lines, setLines] = useState([]);
|
|
14
14
|
const [prompt, setPrompt] = useState();
|
|
@@ -19,6 +19,8 @@ export function App({ client, onExit, }) {
|
|
|
19
19
|
const verdictRef = useRef(undefined);
|
|
20
20
|
const tearingRef = useRef(false);
|
|
21
21
|
useEffect(() => {
|
|
22
|
+
if (initialOms)
|
|
23
|
+
return; // provided by the caller's pre-flight discovery
|
|
22
24
|
let live = true;
|
|
23
25
|
void client.discover().then((r) => {
|
|
24
26
|
if (live)
|
|
@@ -27,7 +29,7 @@ export function App({ client, onExit, }) {
|
|
|
27
29
|
return () => {
|
|
28
30
|
live = false;
|
|
29
31
|
};
|
|
30
|
-
}, [client]);
|
|
32
|
+
}, [client, initialOms]);
|
|
31
33
|
const run = (om) => {
|
|
32
34
|
const s = client.run(om.file);
|
|
33
35
|
sessionRef.current = s;
|
package/dist/core/ActionRun.js
CHANGED
|
@@ -131,8 +131,13 @@ export class ActionRun {
|
|
|
131
131
|
if (win())
|
|
132
132
|
resolve(undefined);
|
|
133
133
|
}, (error) => {
|
|
134
|
-
if (win())
|
|
135
|
-
|
|
134
|
+
if (!win())
|
|
135
|
+
return;
|
|
136
|
+
// Awaiting the `error` event hands back the error; any other gate rejects on failure.
|
|
137
|
+
if (key === "error")
|
|
138
|
+
resolve(error);
|
|
139
|
+
else
|
|
140
|
+
reject(error);
|
|
136
141
|
});
|
|
137
142
|
});
|
|
138
143
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omkit",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A tiny runtime for developer workflows: typed actions that pass runtime capabilities and emit typed events, under one run and one shared log. The operational/runtime layer to tskb's knowledge layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|