kvman 0.1.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 (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +17 -0
  3. package/dist/arguments.d.ts +20 -0
  4. package/dist/arguments.d.ts.map +1 -0
  5. package/dist/arguments.js +80 -0
  6. package/dist/arguments.js.map +1 -0
  7. package/dist/browser.d.ts +7 -0
  8. package/dist/browser.d.ts.map +1 -0
  9. package/dist/browser.js +18 -0
  10. package/dist/browser.js.map +1 -0
  11. package/dist/bundled.d.ts +3 -0
  12. package/dist/bundled.d.ts.map +1 -0
  13. package/dist/bundled.js +28 -0
  14. package/dist/bundled.js.map +1 -0
  15. package/dist/hand-over.d.ts +11 -0
  16. package/dist/hand-over.d.ts.map +1 -0
  17. package/dist/hand-over.js +40 -0
  18. package/dist/hand-over.js.map +1 -0
  19. package/dist/lock-file.d.ts +19 -0
  20. package/dist/lock-file.d.ts.map +1 -0
  21. package/dist/lock-file.js +105 -0
  22. package/dist/lock-file.js.map +1 -0
  23. package/dist/main.d.ts +3 -0
  24. package/dist/main.d.ts.map +1 -0
  25. package/dist/main.js +36 -0
  26. package/dist/main.js.map +1 -0
  27. package/dist/preset-lookup.d.ts +18 -0
  28. package/dist/preset-lookup.d.ts.map +1 -0
  29. package/dist/preset-lookup.js +47 -0
  30. package/dist/preset-lookup.js.map +1 -0
  31. package/dist/run-kvman.d.ts +13 -0
  32. package/dist/run-kvman.d.ts.map +1 -0
  33. package/dist/run-kvman.js +70 -0
  34. package/dist/run-kvman.js.map +1 -0
  35. package/dist/start-kernel.d.ts +6 -0
  36. package/dist/start-kernel.d.ts.map +1 -0
  37. package/dist/start-kernel.js +41 -0
  38. package/dist/start-kernel.js.map +1 -0
  39. package/dist/stop-signals.d.ts +7 -0
  40. package/dist/stop-signals.d.ts.map +1 -0
  41. package/dist/stop-signals.js +29 -0
  42. package/dist/stop-signals.js.map +1 -0
  43. package/dist/terminal.d.ts +3 -0
  44. package/dist/terminal.d.ts.map +1 -0
  45. package/dist/terminal.js +13 -0
  46. package/dist/terminal.js.map +1 -0
  47. package/dist/trust-prompt.d.ts +8 -0
  48. package/dist/trust-prompt.d.ts.map +1 -0
  49. package/dist/trust-prompt.js +25 -0
  50. package/dist/trust-prompt.js.map +1 -0
  51. package/package.json +35 -0
  52. package/presets/coder.json +14 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmed Deif
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # kvman
2
+
3
+ An app that works like a small operating system for every kind of user: a kernel runs extensions, and a preset shapes them into the app a person uses. Every feature is an extension, including the web UI and the AI agent.
4
+
5
+ ```
6
+ npm i -g kvman
7
+ kvman
8
+ ```
9
+
10
+ kvman runs in the foreground on `127.0.0.1:3737` and opens the folder you start it from as a workspace. It needs Node.js 24, on Linux, macOS, or Windows.
11
+
12
+ - [User guide](https://github.com/ibndeif/kvman/blob/main/docs/user-guide/README.md): install, connect a model, chat with the agent, manage settings.
13
+ - [Developer documentation](https://github.com/ibndeif/kvman/blob/main/docs/developers/README.md): build extensions and presets.
14
+
15
+ ## License
16
+
17
+ MIT
@@ -0,0 +1,20 @@
1
+ import type { LogLevel } from '@kvman/kernel';
2
+ export type RunArguments = {
3
+ kind: 'run';
4
+ mode: 'web' | undefined;
5
+ preset: string | undefined;
6
+ home: string | undefined;
7
+ port: number | undefined;
8
+ yes: boolean;
9
+ open: boolean;
10
+ logLevel: LogLevel;
11
+ };
12
+ export type CliArguments = {
13
+ kind: 'help';
14
+ } | {
15
+ kind: 'version';
16
+ } | RunArguments;
17
+ export declare const usage = "Usage:\n kvman [--mode web] [--preset coder] [--home <dir>] [--port <n>] [--yes] [--no-open] [--log-level <level>]\n kvman --help | --version\n\nOptions:\n --mode <mode> How kvman runs: web (the default).\n --preset <preset> A bundled preset, <home>/presets/<name>.json, or a preset file such as ./app.json. Default: coder.\n --home <dir> kvman's home folder. Default: KVMAN_HOME, else ~/.kvman.\n --port <n> The port to listen on (0 picks a free one). Default: the kernel.port setting (3737).\n --yes Accept new extension versions without asking.\n --no-open Don't open the browser.\n --log-level <level> debug, info (the default), warn, or error.\n --help Show this help.\n --version Show kvman's version.\n";
18
+ export declare function parseArguments(argv: readonly string[]): CliArguments;
19
+ export declare function kvmanHome(flag: string | undefined, environment: NodeJS.ProcessEnv, userFolder?: string): string;
20
+ //# sourceMappingURL=arguments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arguments.d.ts","sourceRoot":"","sources":["../src/arguments.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAK9C,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,KAAK,GAAG,SAAS,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,GAAG,EAAE,OAAO,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GAAG,YAAY,CAAC;AAEjF,eAAO,MAAM,KAAK,8yBAcjB,CAAC;AA0CF,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,YAAY,CAcpE;AAGD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,GAAE,MAAqB,GAAG,MAAM,CAG7H"}
@@ -0,0 +1,80 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ import { parseArgs } from 'node:util';
4
+ import { ProblemError, z } from '@kvman/sdk';
5
+ export const usage = `Usage:
6
+ kvman [--mode web] [--preset coder] [--home <dir>] [--port <n>] [--yes] [--no-open] [--log-level <level>]
7
+ kvman --help | --version
8
+
9
+ Options:
10
+ --mode <mode> How kvman runs: web (the default).
11
+ --preset <preset> A bundled preset, <home>/presets/<name>.json, or a preset file such as ./app.json. Default: coder.
12
+ --home <dir> kvman's home folder. Default: KVMAN_HOME, else ~/.kvman.
13
+ --port <n> The port to listen on (0 picks a free one). Default: the kernel.port setting (3737).
14
+ --yes Accept new extension versions without asking.
15
+ --no-open Don't open the browser.
16
+ --log-level <level> debug, info (the default), warn, or error.
17
+ --help Show this help.
18
+ --version Show kvman's version.
19
+ `;
20
+ const flags = {
21
+ mode: { type: 'string' },
22
+ preset: { type: 'string' },
23
+ home: { type: 'string' },
24
+ port: { type: 'string' },
25
+ yes: { type: 'boolean' },
26
+ 'no-open': { type: 'boolean' },
27
+ 'log-level': { type: 'string' },
28
+ help: { type: 'boolean' },
29
+ version: { type: 'boolean' },
30
+ };
31
+ const modeSchema = z.literal('web', { error: 'The mode must be web.' }).optional();
32
+ const logLevelSchema = z.enum(['debug', 'info', 'warn', 'error'], { error: 'The log level must be debug, info, warn, or error.' }).default('info');
33
+ const portSchema = z
34
+ .string()
35
+ .regex(/^\d+$/, { error: 'The port must be a whole number from 0 to 65535.' })
36
+ .transform(Number)
37
+ .pipe(z.number().max(65_535, { error: 'The port must be a whole number from 0 to 65535.' }))
38
+ .optional();
39
+ function invalid(message) {
40
+ return new ProblemError({ code: 'VALIDATION_FAILED', message });
41
+ }
42
+ function parsed(schema, value) {
43
+ const result = schema.safeParse(value);
44
+ if (!result.success)
45
+ throw invalid(result.error.issues.map((issue) => issue.message).join(' '));
46
+ return result.data;
47
+ }
48
+ function readFlags(argv) {
49
+ try {
50
+ return parseArgs({ args: [...argv], options: flags, strict: true, allowPositionals: false });
51
+ }
52
+ catch (error) {
53
+ if (error instanceof TypeError)
54
+ throw invalid(error.message);
55
+ throw error;
56
+ }
57
+ }
58
+ export function parseArguments(argv) {
59
+ const { values } = readFlags(argv);
60
+ if (values.help === true)
61
+ return { kind: 'help' };
62
+ if (values.version === true)
63
+ return { kind: 'version' };
64
+ return {
65
+ kind: 'run',
66
+ mode: parsed(modeSchema, values.mode),
67
+ preset: values.preset,
68
+ home: values.home,
69
+ port: parsed(portSchema, values.port),
70
+ yes: values.yes === true,
71
+ open: values['no-open'] !== true,
72
+ logLevel: parsed(logLevelSchema, values['log-level']),
73
+ };
74
+ }
75
+ // `--home`, else `KVMAN_HOME`, else `~/.kvman` (plan 01 §1.3).
76
+ export function kvmanHome(flag, environment, userFolder = os.homedir()) {
77
+ const chosen = flag ?? environment['KVMAN_HOME'];
78
+ return chosen === undefined || chosen === '' ? path.join(userFolder, '.kvman') : path.resolve(chosen);
79
+ }
80
+ //# sourceMappingURL=arguments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arguments.js","sourceRoot":"","sources":["../src/arguments.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type BrowserCommand = {
2
+ command: string;
3
+ args: string[];
4
+ };
5
+ export declare function browserCommand(platform: NodeJS.Platform, url: string): BrowserCommand;
6
+ export declare function openBrowser(platform: NodeJS.Platform, url: string, failed: (reason: string) => void): void;
7
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,cAAc,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEjE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,cAAc,CAIrF;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAO1G"}
@@ -0,0 +1,18 @@
1
+ import { spawn } from 'node:child_process';
2
+ export function browserCommand(platform, url) {
3
+ if (platform === 'darwin')
4
+ return { command: 'open', args: [url] };
5
+ if (platform === 'win32')
6
+ return { command: 'cmd', args: ['/c', 'start', '""', url] };
7
+ return { command: 'xdg-open', args: [url] };
8
+ }
9
+ export function openBrowser(platform, url, failed) {
10
+ const { command, args } = browserCommand(platform, url);
11
+ const child = spawn(command, args, { stdio: 'ignore', windowsHide: true, windowsVerbatimArguments: platform === 'win32' });
12
+ child.once('error', (error) => failed(error.message));
13
+ child.once('exit', (code) => {
14
+ if (code !== 0)
15
+ failed(`${command} exited with code ${String(code)}`);
16
+ });
17
+ }
18
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAO3C,MAAM,UAAU,cAAc,CAAC,QAAyB,EAAE,GAAW;IACnE,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IACnE,IAAI,QAAQ,KAAK,OAAO;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;IACtF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,QAAyB,EAAE,GAAW,EAAE,MAAgC;IAClG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,EAAE,wBAAwB,EAAE,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;IAC3H,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE;QAC1B,IAAI,IAAI,KAAK,CAAC;YAAE,MAAM,CAAC,GAAG,OAAO,qBAAqB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare const bundledPresetsFolder: string;
2
+ export declare function bundledExtensions(folder?: string): ReadonlyMap<string, string>;
3
+ //# sourceMappingURL=bundled.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled.d.ts","sourceRoot":"","sources":["../src/bundled.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,oBAAoB,QAAsC,CAAC;AASxE,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,MAAsB,GAAG,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAU7F"}
@@ -0,0 +1,28 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createRequire } from 'node:module';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { z } from '@kvman/sdk';
6
+ // What ships with kvman (plan 01 §1.4, ADR 0026): the bundled presets in the package's own `presets/`, and the bundled
7
+ // extensions, which are the package's dependencies that have a `kvman` field. Each is found as Node resolves any
8
+ // module, so the repository and an installed kvman take the same path.
9
+ const packageFolder = fileURLToPath(new URL('../', import.meta.url));
10
+ export const bundledPresetsFolder = path.join(packageFolder, 'presets');
11
+ const packageSchema = z.object({ dependencies: z.record(z.string(), z.string()).optional() });
12
+ const dependencySchema = z.object({ kvman: z.unknown().optional() });
13
+ function readJson(file) {
14
+ return JSON.parse(readFileSync(file, 'utf8'));
15
+ }
16
+ export function bundledExtensions(folder = packageFolder) {
17
+ const manifestFile = path.join(folder, 'package.json');
18
+ const resolveFromPackage = createRequire(manifestFile).resolve;
19
+ const extensions = new Map();
20
+ for (const name of Object.keys(packageSchema.parse(readJson(manifestFile)).dependencies ?? {})) {
21
+ const dependencyManifest = resolveFromPackage(`${name}/package.json`);
22
+ if (dependencySchema.parse(readJson(dependencyManifest)).kvman === undefined)
23
+ continue;
24
+ extensions.set(name, path.dirname(dependencyManifest));
25
+ }
26
+ return extensions;
27
+ }
28
+ //# sourceMappingURL=bundled.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundled.js","sourceRoot":"","sources":["../src/bundled.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAE/B,uHAAuH;AACvH,iHAAiH;AACjH,uEAAuE;AAEvE,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;AAExE,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAC9F,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AAErE,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB,aAAa;IAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvD,MAAM,kBAAkB,GAAG,aAAa,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC;IAC/D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC7C,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/F,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,GAAG,IAAI,eAAe,CAAC,CAAC;QACtE,IAAI,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS;YAAE,SAAS;QACvF,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type Workspace } from '@kvman/sdk';
2
+ import type { Lock } from './lock-file.ts';
3
+ export type GivenFlags = {
4
+ preset: string | undefined;
5
+ mode: 'web' | undefined;
6
+ };
7
+ export declare function handOver(lock: Lock, folder: string, given: GivenFlags): Promise<{
8
+ port: number;
9
+ workspace: Workspace;
10
+ }>;
11
+ //# sourceMappingURL=hand-over.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hand-over.d.ts","sourceRoot":"","sources":["../src/hand-over.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6E,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACvH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAK3C,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,IAAI,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,CAAC;AAgCjF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAO7H"}
@@ -0,0 +1,40 @@
1
+ import { envelopeSchema, healthSchema, ProblemError, workspaceSchema, z } from '@kvman/sdk';
2
+ function running(lock, message, params = {}) {
3
+ return new ProblemError({ code: 'KVMAN_RUNNING', message, params: { pid: lock.pid, ...params } });
4
+ }
5
+ async function call(lock, port, route, input, output) {
6
+ let response;
7
+ try {
8
+ response = await fetch(`http://127.0.0.1:${String(port)}/api/${route}`, {
9
+ method: 'POST',
10
+ headers: { 'content-type': 'application/json' },
11
+ body: JSON.stringify({ input }),
12
+ });
13
+ }
14
+ catch {
15
+ throw running(lock, `Another kvman (pid ${String(lock.pid)}) holds this home's lock, but it doesn't answer on port ${String(port)}.`, { port });
16
+ }
17
+ const answer = envelopeSchema({ output, jobId: z.string() }).parse(await response.json());
18
+ if (!answer.ok)
19
+ throw new ProblemError(answer.problem);
20
+ return answer.output;
21
+ }
22
+ function checkFlags(lock, health, given) {
23
+ if (given.preset !== undefined && given.preset !== health.preset) {
24
+ throw running(lock, `Another kvman (pid ${String(lock.pid)}) runs the preset ${health.preset} on this home, not ${given.preset}.`, { preset: health.preset });
25
+ }
26
+ if (given.mode !== undefined && given.mode !== health.mode) {
27
+ throw running(lock, `Another kvman (pid ${String(lock.pid)}) runs in ${health.mode} mode on this home, not ${given.mode}.`, { mode: health.mode });
28
+ }
29
+ }
30
+ // Opens `folder` in the running kvman and returns its workspace and port.
31
+ export async function handOver(lock, folder, given) {
32
+ const port = lock.port;
33
+ if (port === undefined)
34
+ throw running(lock, `Another kvman (pid ${String(lock.pid)}) is still starting on this home.`);
35
+ const health = await call(lock, port, 'queries/kernel.health.get', {}, healthSchema);
36
+ checkFlags(lock, health, given);
37
+ const workspace = await call(lock, port, 'commands/kernel.workspace.open', { path: folder }, workspaceSchema);
38
+ return { port, workspace };
39
+ }
40
+ //# sourceMappingURL=hand-over.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hand-over.js","sourceRoot":"","sources":["../src/hand-over.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { z } from '@kvman/sdk';
2
+ declare const lockSchema: z.ZodObject<{
3
+ pid: z.ZodNumber;
4
+ port: z.ZodOptional<z.ZodNumber>;
5
+ }, z.core.$strip>;
6
+ export type Lock = z.infer<typeof lockSchema>;
7
+ export type TakenLock = {
8
+ kind: 'taken';
9
+ setPort(port: number): void;
10
+ release(): void;
11
+ };
12
+ export type LockTaking = TakenLock | {
13
+ kind: 'held';
14
+ lock: Lock;
15
+ };
16
+ export declare function isAlive(pid: number): boolean;
17
+ export declare function takeLock(home: string, pid?: number, alive?: (pid: number) => boolean): LockTaking;
18
+ export {};
19
+ //# sourceMappingURL=lock-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-file.d.ts","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,YAAY,CAAC;AAO/B,QAAA,MAAM,UAAU;;;iBAAuG,CAAC;AAExH,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC;AAExF,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC;AAMlE,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAS5C;AA6DD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAoB,EAAE,KAAK,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAiB,GAAG,UAAU,CAUvH"}
@@ -0,0 +1,105 @@
1
+ import { linkSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { z } from '@kvman/sdk';
4
+ // `kvman.lock` (plan 01 §1.3, ADR 0009, 43): `{ pid }` from start, `{ pid, port }` once kvman listens. A lock is only
5
+ // ever put in place whole, by hard-linking a finished file (which fails when a lock exists). A stale lock is moved
6
+ // aside before it's removed; if what was moved turns out to be another kvman's fresh lock, it's put back, so two kvmen
7
+ // replacing one stale lock can't both take it.
8
+ const lockSchema = z.object({ pid: z.number().int().positive(), port: z.number().int().min(1).max(65_535).optional() });
9
+ function hasCode(error, code) {
10
+ return error instanceof Error && 'code' in error && error.code === code;
11
+ }
12
+ export function isAlive(pid) {
13
+ try {
14
+ process.kill(pid, 0);
15
+ return true;
16
+ }
17
+ catch (error) {
18
+ if (hasCode(error, 'ESRCH'))
19
+ return false;
20
+ if (hasCode(error, 'EPERM'))
21
+ return true;
22
+ throw error;
23
+ }
24
+ }
25
+ function readLock(file) {
26
+ try {
27
+ const text = readFileSync(file, 'utf8');
28
+ const result = lockSchema.safeParse(JSON.parse(text));
29
+ return { text, lock: result.success ? result.data : undefined };
30
+ }
31
+ catch (error) {
32
+ if (hasCode(error, 'ENOENT'))
33
+ return undefined;
34
+ if (error instanceof SyntaxError)
35
+ return { text: '', lock: undefined };
36
+ throw error;
37
+ }
38
+ }
39
+ // Writes a finished file beside the lock and links it into place; false when a lock is already there.
40
+ function placeLock(file, lock) {
41
+ const draft = `${file}.${String(process.pid)}.draft`;
42
+ writeFileSync(draft, JSON.stringify(lock));
43
+ try {
44
+ linkSync(draft, file);
45
+ return true;
46
+ }
47
+ catch (error) {
48
+ if (hasCode(error, 'EEXIST'))
49
+ return false;
50
+ throw error;
51
+ }
52
+ finally {
53
+ rmSync(draft, { force: true });
54
+ }
55
+ }
56
+ function removeStale(file, staleText) {
57
+ const aside = `${file}.${String(process.pid)}.stale`;
58
+ try {
59
+ renameSync(file, aside);
60
+ }
61
+ catch (error) {
62
+ if (hasCode(error, 'ENOENT'))
63
+ return;
64
+ throw error;
65
+ }
66
+ if (readFileSync(aside, 'utf8') !== staleText) {
67
+ try {
68
+ linkSync(aside, file);
69
+ }
70
+ catch (error) {
71
+ if (!hasCode(error, 'EEXIST'))
72
+ throw error;
73
+ }
74
+ }
75
+ rmSync(aside, { force: true });
76
+ }
77
+ function taken(file, pid) {
78
+ return {
79
+ kind: 'taken',
80
+ setPort: (port) => {
81
+ const draft = `${file}.${String(pid)}.draft`;
82
+ writeFileSync(draft, JSON.stringify({ pid, port }));
83
+ renameSync(draft, file);
84
+ },
85
+ release: () => {
86
+ if (readLock(file)?.lock?.pid === pid)
87
+ rmSync(file, { force: true });
88
+ },
89
+ };
90
+ }
91
+ export function takeLock(home, pid = process.pid, alive = isAlive) {
92
+ mkdirSync(home, { recursive: true });
93
+ const file = path.join(home, 'kvman.lock');
94
+ for (;;) {
95
+ if (placeLock(file, { pid }))
96
+ return taken(file, pid);
97
+ const found = readLock(file);
98
+ if (found === undefined)
99
+ continue;
100
+ if (found.lock !== undefined && alive(found.lock.pid))
101
+ return { kind: 'held', lock: found.lock };
102
+ removeStale(file, found.text);
103
+ }
104
+ }
105
+ //# sourceMappingURL=lock-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock-file.js","sourceRoot":"","sources":["../src/lock-file.ts"],"names":[],"mappings":"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"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
package/dist/main.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ import os from 'node:os';
3
+ import { kernelVersion } from '@kvman/kernel';
4
+ import { ProblemError } from '@kvman/sdk';
5
+ import { parseArguments, usage } from "./arguments.js";
6
+ import { runKvman } from "./run-kvman.js";
7
+ import { failureLines } from "./terminal.js";
8
+ // The `kvman` bin (plan 01 §1.2): 0 after a clean stop or a hand-over, 1 after a failed start (plan 02 §2.14), and 130
9
+ // after a second Ctrl+C (ADR 0009, 50).
10
+ async function main(argv) {
11
+ const args = parseArguments(argv);
12
+ if (args.kind === 'help') {
13
+ process.stdout.write(usage);
14
+ return 0;
15
+ }
16
+ if (args.kind === 'version') {
17
+ process.stdout.write(`${kernelVersion()}\n`);
18
+ return 0;
19
+ }
20
+ return runKvman(args, {
21
+ variables: process.env,
22
+ startFolder: process.cwd(),
23
+ userFolder: os.homedir(),
24
+ platform: process.platform,
25
+ terminal: { input: process.stdin, output: process.stdout, isTerminal: process.stdin.isTTY === true && process.stdout.isTTY === true },
26
+ errors: process.stderr,
27
+ exitAtOnce: (code) => process.exit(code),
28
+ });
29
+ }
30
+ main(process.argv.slice(2)).then((code) => {
31
+ process.exitCode = code;
32
+ }, (error) => {
33
+ process.stderr.write(error instanceof ProblemError ? failureLines(error.problem) : `${error instanceof Error ? (error.stack ?? error.message) : String(error)}\n`);
34
+ process.exitCode = 1;
35
+ });
36
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,uHAAuH;AACvH,wCAAwC;AAExC,KAAK,UAAU,IAAI,CAAC,IAAuB;IACzC,MAAM,IAAI,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,aAAa,EAAE,IAAI,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,EAAE;QACpB,SAAS,EAAE,OAAO,CAAC,GAAG;QACtB,WAAW,EAAE,OAAO,CAAC,GAAG,EAAE;QAC1B,UAAU,EAAE,EAAE,CAAC,OAAO,EAAE;QACxB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE;QACrI,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;KACzC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAC9B,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,YAAY,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnK,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { PresetSource } from '@kvman/kernel';
2
+ import { type Preset } from '@kvman/sdk';
3
+ export type PresetFolders = {
4
+ bundled: string;
5
+ home: string;
6
+ start: string;
7
+ };
8
+ export type FoundPreset = {
9
+ preset: Preset;
10
+ presetFolder: string;
11
+ source: PresetSource;
12
+ };
13
+ export declare const defaultPreset = "coder";
14
+ export declare function isPresetFile(value: string): boolean;
15
+ /** The file an edit of the preset `value` writes: the preset file given, or `<home>/presets/<name>.json` (ADR 0024, 5). */
16
+ export declare function editableFile(value: string, folders: PresetFolders): string;
17
+ export declare function findPreset(value: string, folders: PresetFolders): FoundPreset;
18
+ //# sourceMappingURL=preset-lookup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset-lookup.d.ts","sourceRoot":"","sources":["../src/preset-lookup.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAA8B,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAOrE,MAAM,MAAM,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,MAAM,MAAM,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC;AAEzF,eAAO,MAAM,aAAa,UAAU,CAAC;AAMrC,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD;AA0BD,2HAA2H;AAC3H,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAE1E;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,WAAW,CAM7E"}
@@ -0,0 +1,47 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { presetSchema, ProblemError } from '@kvman/sdk';
4
+ export const defaultPreset = 'coder';
5
+ function invalid(message, params) {
6
+ return new ProblemError({ code: 'VALIDATION_FAILED', message, params });
7
+ }
8
+ export function isPresetFile(value) {
9
+ return value.includes('/') || value.includes('\\') || value.endsWith('.json');
10
+ }
11
+ function readPreset(file, source) {
12
+ let parsed;
13
+ try {
14
+ parsed = JSON.parse(readFileSync(file, 'utf8'));
15
+ }
16
+ catch (error) {
17
+ const reason = error instanceof Error ? error.message : String(error);
18
+ throw invalid(`The preset ${file} can't be read (${reason}).`, { file });
19
+ }
20
+ const result = presetSchema.safeParse(parsed);
21
+ if (!result.success) {
22
+ const issues = result.error.issues.map((issue) => `${issue.path.join('.') || 'preset'}: ${issue.message}`).join('; ');
23
+ throw invalid(`The preset ${file} is invalid (${issues}).`, { file });
24
+ }
25
+ return { preset: result.data, presetFolder: path.dirname(file), source };
26
+ }
27
+ function namedPreset(name, folders) {
28
+ const own = path.join(folders.home, 'presets', `${name}.json`);
29
+ if (existsSync(own))
30
+ return readPreset(own, { origin: 'home', file: own });
31
+ const bundled = path.join(folders.bundled, `${name}.json`);
32
+ if (existsSync(bundled))
33
+ return readPreset(bundled, { origin: 'bundled' });
34
+ throw invalid(`There is no preset named ${name}: neither a bundled one nor ${own}.`, { preset: name });
35
+ }
36
+ /** The file an edit of the preset `value` writes: the preset file given, or `<home>/presets/<name>.json` (ADR 0024, 5). */
37
+ export function editableFile(value, folders) {
38
+ return isPresetFile(value) ? path.resolve(folders.start, value) : path.join(folders.home, 'presets', `${value}.json`);
39
+ }
40
+ export function findPreset(value, folders) {
41
+ if (isPresetFile(value)) {
42
+ const file = path.resolve(folders.start, value);
43
+ return readPreset(file, { origin: 'file', file });
44
+ }
45
+ return namedPreset(value, folders);
46
+ }
47
+ //# sourceMappingURL=preset-lookup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset-lookup.js","sourceRoot":"","sources":["../src/preset-lookup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { type RunArguments } from './arguments.ts';
2
+ import type { Terminal } from './trust-prompt.ts';
3
+ export type CliEnvironment = {
4
+ variables: NodeJS.ProcessEnv;
5
+ startFolder: string;
6
+ userFolder: string;
7
+ platform: NodeJS.Platform;
8
+ terminal: Terminal;
9
+ errors: NodeJS.WritableStream;
10
+ exitAtOnce: (code: number) => never;
11
+ };
12
+ export declare function runKvman(args: RunArguments, environment: CliEnvironment): Promise<number>;
13
+ //# sourceMappingURL=run-kvman.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-kvman.d.ts","sourceRoot":"","sources":["../src/run-kvman.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAMlD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CACrC,CAAC;AA2CF,wBAAsB,QAAQ,CAAC,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB/F"}
@@ -0,0 +1,70 @@
1
+ import { discardPresetBackup, startHttp } from '@kvman/kernel';
2
+ import { kvmanHome } from "./arguments.js";
3
+ import { openBrowser } from "./browser.js";
4
+ import { bundledPresetsFolder } from "./bundled.js";
5
+ import { handOver } from "./hand-over.js";
6
+ import { takeLock } from "./lock-file.js";
7
+ import { defaultPreset, editableFile, findPreset } from "./preset-lookup.js";
8
+ import { startOrUndo } from "./start-kernel.js";
9
+ import { listenForStop } from "./stop-signals.js";
10
+ function workspaceUrl(port, workspaceId) {
11
+ return `http://127.0.0.1:${String(port)}/?workspace=${encodeURIComponent(workspaceId)}`;
12
+ }
13
+ async function joinRunning(args, lock, folders, environment) {
14
+ const preset = args.preset === undefined ? undefined : findPreset(args.preset, folders).preset.name;
15
+ const { port, workspace } = await handOver(lock, environment.startFolder, { preset, mode: args.mode });
16
+ const url = workspaceUrl(port, workspace.id);
17
+ environment.terminal.output.write(`kvman is already running; this folder is open at ${url}\n`);
18
+ if (args.open)
19
+ openBrowser(environment.platform, url, (reason) => environment.errors.write(`The browser could not be opened (${reason}).\n`));
20
+ return 0;
21
+ }
22
+ // A good start (the port is listening) deletes the backup of the last edit (ADR 0024, 4).
23
+ async function serve(args, kernel, lock, environment, run) {
24
+ const stop = listenForStop(() => {
25
+ lock.release();
26
+ environment.exitAtOnce(130);
27
+ });
28
+ try {
29
+ const http = await startHttp(kernel, { port: args.port ?? kernel.web.port() }).catch(async (error) => {
30
+ await kernel.close();
31
+ throw error;
32
+ });
33
+ lock.setPort(http.port);
34
+ discardPresetBackup(run.presetFile);
35
+ const url = workspaceUrl(http.port, kernel.startWorkspace.id);
36
+ environment.terminal.output.write(`kvman is running at ${url}\n`);
37
+ if (run.open)
38
+ openBrowser(environment.platform, url, (reason) => kernel.web.logger.warn('The browser could not be opened.', { reason }));
39
+ const requested = await Promise.race([stop.stopRequested.then(() => 'stopped'), kernel.restartRequested.then(() => 'restart')]);
40
+ const closing = http.close();
41
+ await kernel.close();
42
+ await closing;
43
+ return stop.isRequested() ? 'stopped' : requested;
44
+ }
45
+ finally {
46
+ stop.dispose();
47
+ }
48
+ }
49
+ export async function runKvman(args, environment) {
50
+ const home = kvmanHome(args.home, environment.variables, environment.userFolder);
51
+ const folders = { bundled: bundledPresetsFolder, home, start: environment.startFolder };
52
+ const lock = takeLock(home);
53
+ if (lock.kind === 'held')
54
+ return joinRunning(args, lock.lock, folders, environment);
55
+ // A program kvman runs (kvcustomizer's `kvman-docs`) finds this kvman through `<home>/kvman.lock` (plan 01 §1.2).
56
+ process.env['KVMAN_HOME'] = home;
57
+ const presetFile = editableFile(args.preset ?? defaultPreset, folders);
58
+ try {
59
+ for (let open = args.open;; open = false) {
60
+ const kernel = await startOrUndo(args, folders, environment);
61
+ if ((await serve(args, kernel, lock, environment, { open, presetFile })) === 'stopped')
62
+ return 0;
63
+ environment.terminal.output.write('kvman is restarting…\n');
64
+ }
65
+ }
66
+ finally {
67
+ lock.release();
68
+ }
69
+ }
70
+ //# sourceMappingURL=run-kvman.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-kvman.js","sourceRoot":"","sources":["../src/run-kvman.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Kernel } from '@kvman/kernel';
2
+ import type { RunArguments } from './arguments.ts';
3
+ import type { CliEnvironment } from './run-kvman.ts';
4
+ import { type PresetFolders } from './preset-lookup.ts';
5
+ export declare function startOrUndo(args: RunArguments, folders: PresetFolders, environment: CliEnvironment): Promise<Kernel>;
6
+ //# sourceMappingURL=start-kernel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-kernel.d.ts","sourceRoot":"","sources":["../src/start-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAE9E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAA2C,KAAK,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4BjG,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAS1H"}
@@ -0,0 +1,41 @@
1
+ import { restorePresetBackup, startKernel } from '@kvman/kernel';
2
+ import { ProblemError } from '@kvman/sdk';
3
+ import { bundledExtensions } from "./bundled.js";
4
+ import { defaultPreset, editableFile, findPreset } from "./preset-lookup.js";
5
+ import { failureLines } from "./terminal.js";
6
+ import { trustDecision } from "./trust-prompt.js";
7
+ // Starting the kernel for a run (plan 02 §2.14, ADR 0024, 5): a start that fails because of the preset, while the
8
+ // backup of the last edit exists, restores the preset and starts once more.
9
+ // The failures that a preset edit can cause; `PORT_IN_USE` and the like are not undone by restoring a file.
10
+ const undoable = new Set(['EXTENSION_INVALID', 'VALIDATION_FAILED']);
11
+ function startOnce(args, folders, environment, rolledBack) {
12
+ const found = findPreset(args.preset ?? defaultPreset, folders);
13
+ return startKernel({
14
+ home: folders.home,
15
+ homeFolder: environment.userFolder,
16
+ preset: found.preset,
17
+ presetFolder: found.presetFolder,
18
+ presetSource: found.source,
19
+ bundled: bundledExtensions(),
20
+ mode: args.mode ?? 'web',
21
+ logLevel: args.logLevel,
22
+ terminalLog: true,
23
+ startFolder: environment.startFolder,
24
+ trust: trustDecision(args.yes, environment.terminal),
25
+ ...(rolledBack === undefined ? {} : { rolledBack }),
26
+ });
27
+ }
28
+ export async function startOrUndo(args, folders, environment) {
29
+ try {
30
+ return await startOnce(args, folders, environment, undefined);
31
+ }
32
+ catch (error) {
33
+ if (!(error instanceof ProblemError) || !undoable.has(error.problem.code))
34
+ throw error;
35
+ if (!restorePresetBackup(editableFile(args.preset ?? defaultPreset, folders)))
36
+ throw error;
37
+ environment.errors.write(`${failureLines(error.problem)}The last change to the preset was undone.\n`);
38
+ return startOnce(args, folders, environment, error.problem);
39
+ }
40
+ }
41
+ //# sourceMappingURL=start-kernel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-kernel.js","sourceRoot":"","sources":["../src/start-kernel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAe,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,YAAY,EAAgB,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAsB,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,kHAAkH;AAClH,4EAA4E;AAE5E,4GAA4G;AAC5G,MAAM,QAAQ,GAAwB,IAAI,GAAG,CAAC,CAAC,mBAAmB,EAAE,mBAAmB,CAAC,CAAC,CAAC;AAE1F,SAAS,SAAS,CAAC,IAAkB,EAAE,OAAsB,EAAE,WAA2B,EAAE,UAA+B;IACzH,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,IAAI,aAAa,EAAE,OAAO,CAAC,CAAC;IAChE,OAAO,WAAW,CAAC;QACjB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,UAAU,EAAE,WAAW,CAAC,UAAU;QAClC,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,YAAY,EAAE,KAAK,CAAC,MAAM;QAC1B,OAAO,EAAE,iBAAiB,EAAE;QAC5B,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,KAAK;QACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,WAAW,CAAC,WAAW;QACpC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,QAAQ,CAAC;QACpD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC;KACpD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAkB,EAAE,OAAsB,EAAE,WAA2B;IACvG,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,CAAC,CAAC,KAAK,YAAY,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;YAAE,MAAM,KAAK,CAAC;QACvF,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,IAAI,aAAa,EAAE,OAAO,CAAC,CAAC;YAAE,MAAM,KAAK,CAAC;QAC3F,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC;QACtG,OAAO,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9D,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export type StopSignals = {
2
+ stopRequested: Promise<void>;
3
+ isRequested(): boolean;
4
+ dispose(): void;
5
+ };
6
+ export declare function listenForStop(atOnce: () => void): StopSignals;
7
+ //# sourceMappingURL=stop-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-signals.d.ts","sourceRoot":"","sources":["../src/stop-signals.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,GAAG;IAAE,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,WAAW,IAAI,OAAO,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAE,CAAC;AAEpG,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,WAAW,CAsB7D"}
@@ -0,0 +1,29 @@
1
+ // Ctrl+C and SIGTERM (plan 02 §2.14, ADR 0009, 50): the first signal starts the stop sequence; a second one stops at
2
+ // once through `atOnce`.
3
+ const signals = ['SIGINT', 'SIGTERM'];
4
+ export function listenForStop(atOnce) {
5
+ let requested = false;
6
+ let request = () => undefined;
7
+ const stopRequested = new Promise((resolve) => {
8
+ request = resolve;
9
+ });
10
+ const onSignal = () => {
11
+ if (requested) {
12
+ atOnce();
13
+ return;
14
+ }
15
+ requested = true;
16
+ request();
17
+ };
18
+ for (const signal of signals)
19
+ process.on(signal, onSignal);
20
+ return {
21
+ stopRequested,
22
+ isRequested: () => requested,
23
+ dispose: () => {
24
+ for (const signal of signals)
25
+ process.off(signal, onSignal);
26
+ },
27
+ };
28
+ }
29
+ //# sourceMappingURL=stop-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-signals.js","sourceRoot":"","sources":["../src/stop-signals.ts"],"names":[],"mappings":"AAAA,qHAAqH;AACrH,yBAAyB;AAEzB,MAAM,OAAO,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAU,CAAC;AAI/C,MAAM,UAAU,aAAa,CAAC,MAAkB;IAC9C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,OAAO,GAAe,GAAG,EAAE,CAAC,SAAS,CAAC;IAC1C,MAAM,aAAa,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAClD,OAAO,GAAG,OAAO,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,GAAS,EAAE;QAC1B,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,EAAE,CAAC;YACT,OAAO;QACT,CAAC;QACD,SAAS,GAAG,IAAI,CAAC;QACjB,OAAO,EAAE,CAAC;IACZ,CAAC,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,OAAO;QAAE,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC3D,OAAO;QACL,aAAa;QACb,WAAW,EAAE,GAAG,EAAE,CAAC,SAAS;QAC5B,OAAO,EAAE,GAAG,EAAE;YACZ,KAAK,MAAM,MAAM,IAAI,OAAO;gBAAE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9D,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Problem } from '@kvman/sdk';
2
+ export declare function failureLines(problem: Problem): string;
3
+ //# sourceMappingURL=terminal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAc1C,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAErD"}
@@ -0,0 +1,13 @@
1
+ // A failed start on the terminal (plan 02 §2.14, ADR 0009, 49): the Problem as `CODE: message`, then a hint for its
2
+ // code, in English.
3
+ const hints = new Map([
4
+ ['KVMAN_RUNNING', 'Use the kvman already running on this home, stop it, or start with --home <dir>. If it is still starting, try again once it has started.'],
5
+ ['PORT_IN_USE', 'Stop what uses the port, or start with --port <n> (0 picks a free one).'],
6
+ ['EXTENSION_INVALID', 'Fix the extension or remove it from the preset. New extension versions need a y at the prompt, or --yes.'],
7
+ ['VALIDATION_FAILED', 'Check the flags (kvman --help), the preset, and its settings.'],
8
+ ]);
9
+ const fallbackHint = 'See logs/kvman.log in the kvman home for details.';
10
+ export function failureLines(problem) {
11
+ return `${problem.code}: ${problem.message}\n${hints.get(problem.code) ?? fallbackHint}\n`;
12
+ }
13
+ //# sourceMappingURL=terminal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AAEA,oHAAoH;AACpH,oBAAoB;AAEpB,MAAM,KAAK,GAAgC,IAAI,GAAG,CAAC;IACjD,CAAC,eAAe,EAAE,0IAA0I,CAAC;IAC7J,CAAC,aAAa,EAAE,yEAAyE,CAAC;IAC1F,CAAC,mBAAmB,EAAE,0GAA0G,CAAC;IACjI,CAAC,mBAAmB,EAAE,+DAA+D,CAAC;CACvF,CAAC,CAAC;AAEH,MAAM,YAAY,GAAG,mDAAmD,CAAC;AAEzE,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,YAAY,IAAI,CAAC;AAC7F,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { TrustDecision } from '@kvman/kernel';
2
+ export type Terminal = {
3
+ input: NodeJS.ReadableStream;
4
+ output: NodeJS.WritableStream;
5
+ isTerminal: boolean;
6
+ };
7
+ export declare function trustDecision(yes: boolean, terminal: Terminal): TrustDecision;
8
+ //# sourceMappingURL=trust-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-prompt.d.ts","sourceRoot":"","sources":["../src/trust-prompt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,aAAa,EAAE,MAAM,eAAe,CAAC;AAMrE,MAAM,MAAM,QAAQ,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC;AAc5G,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,aAAa,CAS7E"}
@@ -0,0 +1,25 @@
1
+ import { createInterface } from 'node:readline';
2
+ function ask(terminal, question) {
3
+ const lines = createInterface({ input: terminal.input, output: terminal.output });
4
+ return new Promise((resolve) => {
5
+ lines.once('close', () => resolve(''));
6
+ lines.once('SIGINT', () => lines.close());
7
+ lines.question(question, (answer) => {
8
+ resolve(answer);
9
+ lines.close();
10
+ });
11
+ });
12
+ }
13
+ export function trustDecision(yes, terminal) {
14
+ return async (versions) => {
15
+ if (yes)
16
+ return true;
17
+ if (!terminal.isTerminal)
18
+ return false;
19
+ const listed = versions.map((version) => ` ${version.name}@${version.version} (${version.source})\n`).join('');
20
+ terminal.output.write(`These extension versions haven't been accepted before:\n${listed}`);
21
+ const answer = await ask(terminal, 'Load them? [y/N] ');
22
+ return answer.trim().toLowerCase() === 'y';
23
+ };
24
+ }
25
+ //# sourceMappingURL=trust-prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-prompt.js","sourceRoot":"","sources":["../src/trust-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAShD,SAAS,GAAG,CAAC,QAAkB,EAAE,QAAgB;IAC/C,MAAM,KAAK,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAClF,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;QACrC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;QACvC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1C,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAClC,OAAO,CAAC,MAAM,CAAC,CAAC;YAChB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAY,EAAE,QAAkB;IAC5D,OAAO,KAAK,EAAE,QAAqC,EAAE,EAAE;QACrD,IAAI,GAAG;YAAE,OAAO,IAAI,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChH,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,2DAA2D,MAAM,EAAE,CAAC,CAAC;QAC3F,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC;IAC7C,CAAC,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "kvman",
3
+ "version": "0.1.0",
4
+ "description": "kvman, an app that works like a small operating system: a kernel runs extensions, and a preset shapes them.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/ibndeif/kvman.git",
9
+ "directory": "packages/cli"
10
+ },
11
+ "homepage": "https://github.com/ibndeif/kvman#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/ibndeif/kvman/issues"
14
+ },
15
+ "type": "module",
16
+ "files": [
17
+ "dist",
18
+ "presets"
19
+ ],
20
+ "bin": {
21
+ "kvman": "./dist/main.js"
22
+ },
23
+ "dependencies": {
24
+ "@kvman/kernel": "0.1.0",
25
+ "@kvman/kvai": "0.1.0",
26
+ "@kvman/kvcoder": "0.1.0",
27
+ "@kvman/kvcustomizer": "0.1.0",
28
+ "@kvman/kvwebui": "0.1.0",
29
+ "@kvman/sdk": "0.1.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsc -p tsconfig.build.json",
33
+ "typecheck": "tsc -p tsconfig.json"
34
+ }
35
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "coder",
3
+ "extensions": {
4
+ "@kvman/kvai": "bundled",
5
+ "@kvman/kvwebui": "bundled",
6
+ "@kvman/kvcoder": "bundled",
7
+ "@kvman/kvcustomizer": "bundled"
8
+ },
9
+ "settings": {
10
+ "kvwebui.title": "kvcoder.app.title",
11
+ "kvwebui.home": "kvcoder.chat",
12
+ "kvai.defaultModel": "anthropic/claude-sonnet-5-5"
13
+ }
14
+ }