use-vibes 0.19.27-dev-cli → 0.19.29-dev-cli
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/esm/bin.js +4 -2
- package/esm/cli/exec/handle-register.d.ts +3 -0
- package/esm/cli/exec/handle-register.d.ts.map +1 -0
- package/esm/cli/exec/handle-register.js +18 -0
- package/esm/cli/exec/info.d.ts +3 -0
- package/esm/cli/exec/info.d.ts.map +1 -0
- package/esm/cli/exec/info.js +18 -0
- package/esm/cli/exec/login.d.ts +3 -0
- package/esm/cli/exec/login.d.ts.map +1 -0
- package/esm/cli/exec/login.js +32 -0
- package/esm/cli/exec/skills.d.ts +3 -0
- package/esm/cli/exec/skills.d.ts.map +1 -0
- package/esm/cli/exec/skills.js +13 -0
- package/esm/cli/exec/system.d.ts +3 -0
- package/esm/cli/exec/system.d.ts.map +1 -0
- package/esm/cli/exec/system.js +23 -0
- package/esm/cli/exec/whoami.d.ts +3 -0
- package/esm/cli/exec/whoami.d.ts.map +1 -0
- package/esm/cli/exec/whoami.js +13 -0
- package/esm/cli/executable.d.ts +13 -0
- package/esm/cli/executable.d.ts.map +1 -0
- package/esm/cli/executable.js +1 -0
- package/esm/commands/cli-output.d.ts.map +1 -1
- package/esm/commands/cli-output.js +1 -0
- package/esm/commands/config.d.ts +13 -0
- package/esm/commands/config.d.ts.map +1 -0
- package/esm/commands/config.js +36 -0
- package/esm/commands/handle-register.d.ts +19 -0
- package/esm/commands/handle-register.d.ts.map +1 -0
- package/esm/commands/handle-register.js +65 -0
- package/esm/commands/info.d.ts +8 -0
- package/esm/commands/info.d.ts.map +1 -0
- package/esm/commands/info.js +25 -0
- package/esm/commands/login-platform-node.d.ts +3 -0
- package/esm/commands/login-platform-node.d.ts.map +1 -0
- package/esm/commands/login-platform-node.js +56 -0
- package/esm/commands/login.d.ts +22 -0
- package/esm/commands/login.d.ts.map +1 -0
- package/esm/commands/login.js +127 -0
- package/esm/commands/resolve-target.d.ts +13 -0
- package/esm/commands/resolve-target.d.ts.map +1 -0
- package/esm/commands/resolve-target.js +42 -0
- package/esm/commands/vibes-api.d.ts +15 -0
- package/esm/commands/vibes-api.d.ts.map +1 -0
- package/esm/commands/vibes-api.js +51 -0
- package/esm/commands/whoami.d.ts +19 -1
- package/esm/commands/whoami.d.ts.map +1 -1
- package/esm/commands/whoami.js +100 -2
- package/esm/dispatcher.d.ts +5 -0
- package/esm/dispatcher.d.ts.map +1 -0
- package/esm/dispatcher.js +47 -0
- package/package.json +2 -3
- package/esm/commands/not-implemented.d.ts +0 -6
- package/esm/commands/not-implemented.d.ts.map +0 -1
- package/esm/commands/not-implemented.js +0 -6
- package/esm/run-cli.d.ts +0 -7
- package/esm/run-cli.d.ts.map +0 -1
- package/esm/run-cli.js +0 -107
package/esm/bin.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Node CLI entry point — compiled by dnt with #!/usr/bin/env node shebang.
|
|
3
3
|
// For Deno, use main.deno.ts instead.
|
|
4
4
|
import { defaultCliOutput } from "./commands/cli-output.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import { nodeLoginPlatform } from "./commands/login-platform-node.js";
|
|
6
|
+
import { dispatch } from "./dispatcher.js";
|
|
7
|
+
await dispatch(process.argv.slice(2), {
|
|
7
8
|
output: defaultCliOutput,
|
|
8
9
|
setExitCode(code) {
|
|
9
10
|
process.exitCode = code;
|
|
10
11
|
},
|
|
12
|
+
loginPlatform: nodeLoginPlatform,
|
|
11
13
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-register.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/handle-register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,kBAAkB,EAAE,iBAgBhC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { runRegisterHandle } from "../../commands/handle-register.js";
|
|
2
|
+
export const handleRegisterExec = {
|
|
3
|
+
name: "handle register",
|
|
4
|
+
description: "Register a handle slug",
|
|
5
|
+
async run(argv, runtime) {
|
|
6
|
+
if (argv.length > 1) {
|
|
7
|
+
runtime.output.stderr("handle register accepts at most one slug argument\n");
|
|
8
|
+
return 1;
|
|
9
|
+
}
|
|
10
|
+
const slug = argv.length > 0 ? argv[0] : undefined;
|
|
11
|
+
const result = await runRegisterHandle({ slug }, runtime.output);
|
|
12
|
+
if (result.isErr()) {
|
|
13
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
return 0;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/info.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,QAAQ,EAAE,iBAgBtB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { runInfo } from "../../commands/info.js";
|
|
2
|
+
export const infoExec = {
|
|
3
|
+
name: "info",
|
|
4
|
+
description: "Show resolved config and target",
|
|
5
|
+
async run(argv, runtime) {
|
|
6
|
+
if (argv.length > 1) {
|
|
7
|
+
runtime.output.stderr("info accepts at most one target argument\n");
|
|
8
|
+
return 1;
|
|
9
|
+
}
|
|
10
|
+
const target = argv.length > 0 ? argv[0] : undefined;
|
|
11
|
+
const result = await runInfo({ target }, runtime.output);
|
|
12
|
+
if (result.isErr()) {
|
|
13
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
return 0;
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/login.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAsB1D,eAAO,MAAM,SAAS,EAAE,iBAYvB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { runLogin } from "../../commands/login.js";
|
|
2
|
+
function parseArgs(argv) {
|
|
3
|
+
let caUrl;
|
|
4
|
+
let timeout = 120;
|
|
5
|
+
let forceRenew = false;
|
|
6
|
+
for (let i = 0; i < argv.length; i++) {
|
|
7
|
+
const arg = argv[i];
|
|
8
|
+
if (arg === "--ca-url" && i + 1 < argv.length) {
|
|
9
|
+
caUrl = argv[++i];
|
|
10
|
+
}
|
|
11
|
+
else if (arg === "--timeout" && i + 1 < argv.length) {
|
|
12
|
+
timeout = parseInt(argv[++i], 10);
|
|
13
|
+
}
|
|
14
|
+
else if (arg === "--force-renew") {
|
|
15
|
+
forceRenew = true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return { caUrl, timeout, forceRenew };
|
|
19
|
+
}
|
|
20
|
+
export const loginExec = {
|
|
21
|
+
name: "login",
|
|
22
|
+
description: "Authenticate with vibes.diy",
|
|
23
|
+
async run(argv, runtime) {
|
|
24
|
+
const args = parseArgs(argv);
|
|
25
|
+
const result = await runLogin(args, runtime.output, runtime.loginPlatform);
|
|
26
|
+
if (result.isErr()) {
|
|
27
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
|
30
|
+
return 0;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,UAAU,EAAE,iBAWxB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { runSkills } from "../../commands/skills.js";
|
|
2
|
+
export const skillsExec = {
|
|
3
|
+
name: "skills",
|
|
4
|
+
description: "List available skills",
|
|
5
|
+
async run(_argv, runtime) {
|
|
6
|
+
const result = await runSkills(runtime.output);
|
|
7
|
+
if (result.isErr()) {
|
|
8
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
9
|
+
return 1;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/system.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,UAAU,EAAE,iBAsBxB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { runSystem } from "../../commands/system.js";
|
|
2
|
+
export const systemExec = {
|
|
3
|
+
name: "system",
|
|
4
|
+
description: "Emit system prompt",
|
|
5
|
+
async run(argv, runtime) {
|
|
6
|
+
let skillsCsv;
|
|
7
|
+
for (let i = 0; i < argv.length; i++) {
|
|
8
|
+
if (argv[i] === "--skills" && i + 1 < argv.length) {
|
|
9
|
+
skillsCsv = argv[++i];
|
|
10
|
+
}
|
|
11
|
+
else if (argv[i] === "--skills") {
|
|
12
|
+
// --skills with no value: fall back to defaults
|
|
13
|
+
skillsCsv = undefined;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
const result = await runSystem({ skillsCsv }, runtime.output);
|
|
17
|
+
if (result.isErr()) {
|
|
18
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
},
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../../src/cli/exec/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,eAAO,MAAM,UAAU,EAAE,iBAWxB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { runWhoami } from "../../commands/whoami.js";
|
|
2
|
+
export const whoamiExec = {
|
|
3
|
+
name: "whoami",
|
|
4
|
+
description: "Print device identity and linked handles",
|
|
5
|
+
async run(_argv, runtime) {
|
|
6
|
+
const result = await runWhoami(runtime.output);
|
|
7
|
+
if (result.isErr()) {
|
|
8
|
+
runtime.output.stderr(String(result.Err()) + "\n");
|
|
9
|
+
return 1;
|
|
10
|
+
}
|
|
11
|
+
return 0;
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CliOutput } from "../commands/cli-output.js";
|
|
2
|
+
import type { LoginPlatform } from "../commands/login.js";
|
|
3
|
+
export interface CliRuntime {
|
|
4
|
+
readonly output: CliOutput;
|
|
5
|
+
readonly setExitCode: (code: number) => void;
|
|
6
|
+
readonly loginPlatform: LoginPlatform;
|
|
7
|
+
}
|
|
8
|
+
export interface CommandExecutable {
|
|
9
|
+
readonly name: string;
|
|
10
|
+
readonly description: string;
|
|
11
|
+
run(argv: string[], runtime: CliRuntime): Promise<number>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=executable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executable.d.ts","sourceRoot":"","sources":["../../src/cli/executable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;CACvC;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-output.d.ts","sourceRoot":"","sources":["../../src/commands/cli-output.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;
|
|
1
|
+
{"version":3,"file":"cli-output.d.ts","sourceRoot":"","sources":["../../src/commands/cli-output.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAID,eAAO,MAAM,gBAAgB,EAAE,SAG9B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
declare const VibesJsonType: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
3
|
+
app: string;
|
|
4
|
+
targets?: Record<string, unknown> | undefined;
|
|
5
|
+
}, {}>;
|
|
6
|
+
export type VibesConfig = typeof VibesJsonType.infer;
|
|
7
|
+
export interface FoundConfig {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly config: VibesConfig;
|
|
10
|
+
}
|
|
11
|
+
export declare function findVibesJson(startDir: string): Promise<Result<FoundConfig>>;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAoB,MAAM,iBAAiB,CAAC;AAG3D,QAAA,MAAM,aAAa;;;MAGjB,CAAC;AAEH,MAAM,MAAM,WAAW,GAAG,OAAO,aAAa,CAAC,KAAK,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CA+BlF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { Result, exception2Result } from "@adviser/cement";
|
|
4
|
+
import { type } from "arktype";
|
|
5
|
+
const VibesJsonType = type({
|
|
6
|
+
app: "string > 0",
|
|
7
|
+
"targets?": "Record<string, unknown>",
|
|
8
|
+
});
|
|
9
|
+
export async function findVibesJson(startDir) {
|
|
10
|
+
let dir = startDir;
|
|
11
|
+
for (;;) {
|
|
12
|
+
const candidate = join(dir, "vibes.json");
|
|
13
|
+
const readResult = await exception2Result(() => readFile(candidate, "utf-8"));
|
|
14
|
+
if (readResult.isErr()) {
|
|
15
|
+
const err = readResult.Err();
|
|
16
|
+
if (typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT") {
|
|
17
|
+
const parent = dirname(dir);
|
|
18
|
+
if (parent === dir) {
|
|
19
|
+
return Result.Err("No vibes.json found (searched up to filesystem root)");
|
|
20
|
+
}
|
|
21
|
+
dir = parent;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
return Result.Err(`Error reading ${candidate}: ${err}`);
|
|
25
|
+
}
|
|
26
|
+
const parseResult = exception2Result(() => JSON.parse(readResult.Ok()));
|
|
27
|
+
if (parseResult.isErr()) {
|
|
28
|
+
return Result.Err(`Invalid JSON in ${candidate}: ${parseResult.Err()}`);
|
|
29
|
+
}
|
|
30
|
+
const validated = VibesJsonType(parseResult.Ok());
|
|
31
|
+
if (validated instanceof type.errors) {
|
|
32
|
+
return Result.Err(`Invalid vibes.json at ${candidate}: ${validated.summary}`);
|
|
33
|
+
}
|
|
34
|
+
return Result.Ok({ path: candidate, config: validated });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import type { CliOutput } from "./cli-output.js";
|
|
3
|
+
import type { ReqRegisterHandle, ResRegisterHandle } from "@vibes.diy/api-types";
|
|
4
|
+
export interface RunRegisterHandleOptions {
|
|
5
|
+
readonly slug?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RegisterHandleDeps {
|
|
8
|
+
readonly api?: RegisterHandleApi;
|
|
9
|
+
}
|
|
10
|
+
export interface RegisterHandleResultLike {
|
|
11
|
+
isErr(): boolean;
|
|
12
|
+
Err(): unknown;
|
|
13
|
+
Ok(): ResRegisterHandle;
|
|
14
|
+
}
|
|
15
|
+
export interface RegisterHandleApi {
|
|
16
|
+
registerHandle(req: Omit<ReqRegisterHandle, "type" | "auth">): Promise<RegisterHandleResultLike>;
|
|
17
|
+
}
|
|
18
|
+
export declare function runRegisterHandle(options: RunRegisterHandleOptions, output: CliOutput, deps?: RegisterHandleDeps): Promise<Result<void>>;
|
|
19
|
+
//# sourceMappingURL=handle-register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-register.d.ts","sourceRoot":"","sources":["../../src/commands/handle-register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEjF,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,IAAI,OAAO,CAAC;IACjB,GAAG,IAAI,OAAO,CAAC;IACf,EAAE,IAAI,iBAAiB,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAClG;AA0CD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,wBAAwB,EACjC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,kBAAuB,GAC5B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAgCvB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import { createCliVibesApi, getCliDashAuth } from "./vibes-api.js";
|
|
3
|
+
function hasMessage(value) {
|
|
4
|
+
if (typeof value !== "object" || value === null) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
if (!("message" in value)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return typeof Reflect.get(value, "message") === "string";
|
|
11
|
+
}
|
|
12
|
+
function toErrorMessage(error) {
|
|
13
|
+
if (typeof error === "string") {
|
|
14
|
+
return error;
|
|
15
|
+
}
|
|
16
|
+
if (hasMessage(error)) {
|
|
17
|
+
return error.message;
|
|
18
|
+
}
|
|
19
|
+
return String(error);
|
|
20
|
+
}
|
|
21
|
+
function toRegisterReq(slug) {
|
|
22
|
+
if (typeof slug === "undefined") {
|
|
23
|
+
return Result.Ok({});
|
|
24
|
+
}
|
|
25
|
+
const trimmed = slug.trim();
|
|
26
|
+
if (trimmed.length === 0) {
|
|
27
|
+
return Result.Err("Handle slug must not be empty");
|
|
28
|
+
}
|
|
29
|
+
const withoutAt = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed;
|
|
30
|
+
if (withoutAt.length === 0) {
|
|
31
|
+
return Result.Err("Handle slug must not be empty");
|
|
32
|
+
}
|
|
33
|
+
return Result.Ok({ userSlug: withoutAt });
|
|
34
|
+
}
|
|
35
|
+
function emitRegistered(output, registered) {
|
|
36
|
+
output.stdout(`handle: @${registered.userSlug}\n`);
|
|
37
|
+
output.stdout(`created: ${registered.created}\n`);
|
|
38
|
+
}
|
|
39
|
+
export async function runRegisterHandle(options, output, deps = {}) {
|
|
40
|
+
const rReq = toRegisterReq(options.slug);
|
|
41
|
+
if (rReq.isErr()) {
|
|
42
|
+
return Result.Err(rReq.Err());
|
|
43
|
+
}
|
|
44
|
+
const api = await (async function resolveApi() {
|
|
45
|
+
if (deps.api) {
|
|
46
|
+
return Result.Ok(deps.api);
|
|
47
|
+
}
|
|
48
|
+
const rAuth = await getCliDashAuth();
|
|
49
|
+
if (rAuth.isErr()) {
|
|
50
|
+
return Result.Err(toErrorMessage(rAuth.Err()));
|
|
51
|
+
}
|
|
52
|
+
return Result.Ok(createCliVibesApi({
|
|
53
|
+
getToken: () => Promise.resolve(rAuth),
|
|
54
|
+
}));
|
|
55
|
+
})();
|
|
56
|
+
if (api.isErr()) {
|
|
57
|
+
return Result.Err(api.Err());
|
|
58
|
+
}
|
|
59
|
+
const rRegistered = await api.Ok().registerHandle(rReq.Ok());
|
|
60
|
+
if (rRegistered.isErr()) {
|
|
61
|
+
return Result.Err(toErrorMessage(rRegistered.Err()));
|
|
62
|
+
}
|
|
63
|
+
emitRegistered(output, rRegistered.Ok());
|
|
64
|
+
return Result.Ok(undefined);
|
|
65
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import type { CliOutput } from "./cli-output.js";
|
|
3
|
+
export interface RunInfoOptions {
|
|
4
|
+
readonly target?: string;
|
|
5
|
+
readonly startDir?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function runInfo(opts: RunInfoOptions, output: CliOutput): Promise<Result<void>>;
|
|
8
|
+
//# sourceMappingURL=info.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"info.d.ts","sourceRoot":"","sources":["../../src/commands/info.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAoB5F"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cwd } from "node:process";
|
|
2
|
+
import { Result } from "@adviser/cement";
|
|
3
|
+
import { findVibesJson } from "./config.js";
|
|
4
|
+
import { resolveTarget } from "./resolve-target.js";
|
|
5
|
+
export async function runInfo(opts, output) {
|
|
6
|
+
const startDir = opts.startDir ?? cwd();
|
|
7
|
+
const found = await findVibesJson(startDir);
|
|
8
|
+
if (found.isErr())
|
|
9
|
+
return Result.Err(found.Err());
|
|
10
|
+
const { path, config } = found.Ok();
|
|
11
|
+
output.stdout(`vibes.json: ${path}\n`);
|
|
12
|
+
output.stdout(`app: ${config.app}\n`);
|
|
13
|
+
// Only resolve fully-qualified targets (owner/app/group) without auth.
|
|
14
|
+
// Bare targets need an owner from login, which isn't implemented yet.
|
|
15
|
+
if (opts.target && opts.target.includes("/")) {
|
|
16
|
+
const resolved = resolveTarget({ app: config.app, handle: "" }, opts.target);
|
|
17
|
+
if (resolved.isErr())
|
|
18
|
+
return Result.Err(resolved.Err());
|
|
19
|
+
output.stdout(`target: ${resolved.Ok().full}\n`);
|
|
20
|
+
}
|
|
21
|
+
else if (opts.target) {
|
|
22
|
+
output.stdout(`target: (requires login to resolve "${opts.target}")\n`);
|
|
23
|
+
}
|
|
24
|
+
return Result.Ok(undefined);
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-platform-node.d.ts","sourceRoot":"","sources":["../../src/commands/login-platform-node.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAQhD,eAAO,MAAM,iBAAiB,EAAE,aA8C/B,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createServer } from "node:http";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { env, platform } from "node:process";
|
|
4
|
+
function closeServer(server) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export const nodeLoginPlatform = {
|
|
10
|
+
serve(opts, handler) {
|
|
11
|
+
let finishedResolve;
|
|
12
|
+
const finished = new Promise((r) => {
|
|
13
|
+
finishedResolve = r;
|
|
14
|
+
});
|
|
15
|
+
const server = createServer(async (req, res) => {
|
|
16
|
+
const url = new URL(req.url ?? "/", `http://${opts.hostname}:${opts.port}`);
|
|
17
|
+
const request = new Request(url.toString(), { method: req.method });
|
|
18
|
+
try {
|
|
19
|
+
const response = await handler(request);
|
|
20
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
21
|
+
const body = await response.text();
|
|
22
|
+
res.end(body);
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
res.writeHead(500);
|
|
26
|
+
res.end(err instanceof Error ? err.message : "Internal error");
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.on("error", () => {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- assigned synchronously in Promise constructor
|
|
31
|
+
finishedResolve();
|
|
32
|
+
});
|
|
33
|
+
opts.signal.addEventListener("abort", () => {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- assigned synchronously in Promise constructor
|
|
35
|
+
server.close(() => finishedResolve());
|
|
36
|
+
});
|
|
37
|
+
server.listen(opts.port, opts.hostname);
|
|
38
|
+
return {
|
|
39
|
+
close() {
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- assigned synchronously in Promise constructor
|
|
41
|
+
closeServer(server).then(finishedResolve, finishedResolve);
|
|
42
|
+
},
|
|
43
|
+
finished,
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
async openBrowser(url) {
|
|
47
|
+
const cmd = platform === "win32" ? "cmd" : platform === "darwin" ? "open" : "xdg-open";
|
|
48
|
+
const args = platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
49
|
+
await new Promise((resolve, reject) => {
|
|
50
|
+
execFile(cmd, args, (err) => (err ? reject(err) : resolve()));
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
getEnv(key) {
|
|
54
|
+
return env[key];
|
|
55
|
+
},
|
|
56
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import type { CliOutput } from "./cli-output.js";
|
|
3
|
+
export interface LoginServer {
|
|
4
|
+
close(): void;
|
|
5
|
+
readonly finished: Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
export interface LoginPlatform {
|
|
8
|
+
readonly serve: (opts: {
|
|
9
|
+
port: number;
|
|
10
|
+
hostname: string;
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
}, handler: (req: Request) => Response | Promise<Response>) => LoginServer;
|
|
13
|
+
readonly openBrowser: (url: string) => Promise<void>;
|
|
14
|
+
readonly getEnv: (key: string) => string | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface LoginOptions {
|
|
17
|
+
readonly caUrl?: string;
|
|
18
|
+
readonly timeout?: number;
|
|
19
|
+
readonly forceRenew?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function runLogin(options: LoginOptions, output: CliOutput, platform: LoginPlatform): Promise<Result<void>>;
|
|
22
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAMzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAMjD,MAAM,WAAW,WAAW;IAC1B,KAAK,IAAI,IAAI,CAAC;IACd,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,WAAW,CAAA;KAAE,EAC7D,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,KACpD,WAAW,CAAC;IACjB,QAAQ,CAAC,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACtD;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED,wBAAsB,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAoIvH"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import { DeviceIdKey, DeviceIdCSR } from "@fireproof/core-device-id";
|
|
3
|
+
import { getKeyBag } from "@fireproof/core-keybag";
|
|
4
|
+
import { CertificatePayloadSchema } from "@fireproof/core-types-base";
|
|
5
|
+
import { ensureSuperThis } from "@fireproof/core-runtime";
|
|
6
|
+
import { decodeJwt } from "jose";
|
|
7
|
+
const DEFAULT_CA_URL = "https://vibes.diy/csr2cert";
|
|
8
|
+
const CA_URL_ENV = "VIBES_DIY_CA_URL";
|
|
9
|
+
const DEFAULT_TIMEOUT_S = 120;
|
|
10
|
+
export async function runLogin(options, output, platform) {
|
|
11
|
+
const sthis = ensureSuperThis();
|
|
12
|
+
const keyBag = await getKeyBag(sthis);
|
|
13
|
+
const existing = await keyBag.getDeviceId();
|
|
14
|
+
// Check if already registered
|
|
15
|
+
if (existing.cert.IsSome() && !options.forceRenew) {
|
|
16
|
+
const jwk = existing.deviceId.unwrap();
|
|
17
|
+
const deviceIdKey = (await DeviceIdKey.createFromJWK(jwk)).unwrap();
|
|
18
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
19
|
+
output.stdout(`Already registered. Device fingerprint: ${fingerprint}\n`);
|
|
20
|
+
output.stdout("Use --force-renew to renew the certificate.\n");
|
|
21
|
+
return Result.Ok(undefined);
|
|
22
|
+
}
|
|
23
|
+
// Step 1: Create or load device key
|
|
24
|
+
let deviceIdKey;
|
|
25
|
+
if (existing.deviceId.IsNone()) {
|
|
26
|
+
output.stdout("Creating device key pair...\n");
|
|
27
|
+
deviceIdKey = await DeviceIdKey.create();
|
|
28
|
+
const jwkPrivate = await deviceIdKey.exportPrivateJWK();
|
|
29
|
+
await keyBag.setDeviceId(jwkPrivate);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const createResult = await DeviceIdKey.createFromJWK(existing.deviceId.unwrap());
|
|
33
|
+
if (createResult.isErr()) {
|
|
34
|
+
return Result.Err(`Failed to load device key: ${createResult.Err()}`);
|
|
35
|
+
}
|
|
36
|
+
deviceIdKey = createResult.Ok();
|
|
37
|
+
}
|
|
38
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
39
|
+
output.stdout(`Device fingerprint: ${fingerprint}\n`);
|
|
40
|
+
// Step 2: Generate CSR
|
|
41
|
+
output.stdout("Generating certificate signing request...\n");
|
|
42
|
+
const deviceIdCSR = new DeviceIdCSR(sthis, deviceIdKey);
|
|
43
|
+
const csrResult = await deviceIdCSR.createCSR({
|
|
44
|
+
commonName: `use-vibes-cli@${fingerprint}`,
|
|
45
|
+
organization: "vibes.diy",
|
|
46
|
+
locality: "SF",
|
|
47
|
+
stateOrProvinceName: "CA",
|
|
48
|
+
countryName: "US",
|
|
49
|
+
});
|
|
50
|
+
if (csrResult.isErr()) {
|
|
51
|
+
return Result.Err(`CSR generation failed: ${csrResult.Err()}`);
|
|
52
|
+
}
|
|
53
|
+
const csrJWS = csrResult.Ok();
|
|
54
|
+
// Step 3: Start localhost callback server
|
|
55
|
+
const state = crypto.randomUUID();
|
|
56
|
+
const port = Math.floor(Math.random() * (65535 - 49152) + 49152);
|
|
57
|
+
const callbackUrl = `http://127.0.0.1:${port}/cert`;
|
|
58
|
+
const timeoutMs = (options.timeout ?? DEFAULT_TIMEOUT_S) * 1000;
|
|
59
|
+
let certResolve;
|
|
60
|
+
const certPromise = new Promise((resolve, _reject) => {
|
|
61
|
+
certResolve = resolve;
|
|
62
|
+
});
|
|
63
|
+
const abortController = new AbortController();
|
|
64
|
+
const server = platform.serve({ port, hostname: "127.0.0.1", signal: abortController.signal }, (req) => {
|
|
65
|
+
const url = new URL(req.url);
|
|
66
|
+
if (url.pathname !== "/cert") {
|
|
67
|
+
return new Response("Not Found", { status: 404 });
|
|
68
|
+
}
|
|
69
|
+
const cert = url.searchParams.get("cert");
|
|
70
|
+
const returnedState = url.searchParams.get("state");
|
|
71
|
+
if (!cert) {
|
|
72
|
+
return new Response("Missing cert parameter", { status: 400 });
|
|
73
|
+
}
|
|
74
|
+
if (returnedState !== state) {
|
|
75
|
+
return new Response("State mismatch", { status: 403 });
|
|
76
|
+
}
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- assigned synchronously in Promise constructor
|
|
78
|
+
certResolve(cert);
|
|
79
|
+
return new Response("Certificate received. You can close this window.", {
|
|
80
|
+
headers: { "Content-Type": "text/plain" },
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
// Step 4: Open browser
|
|
84
|
+
const caUrl = options.caUrl ?? platform.getEnv(CA_URL_ENV) ?? DEFAULT_CA_URL;
|
|
85
|
+
const browserUrl = `${caUrl}?csr=${encodeURIComponent(csrJWS)}&returnUrl=${encodeURIComponent(callbackUrl)}&state=${encodeURIComponent(state)}`;
|
|
86
|
+
output.stdout(`\nOpen this URL in your browser to authorize:\n${browserUrl}\n\n`);
|
|
87
|
+
// Try to open browser automatically
|
|
88
|
+
try {
|
|
89
|
+
await platform.openBrowser(browserUrl);
|
|
90
|
+
output.stdout("Browser opened. Waiting for authorization...\n");
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
output.stdout("Waiting for authorization...\n");
|
|
94
|
+
}
|
|
95
|
+
// Step 5: Wait for certificate with timeout
|
|
96
|
+
let receivedCert;
|
|
97
|
+
try {
|
|
98
|
+
receivedCert = await Promise.race([
|
|
99
|
+
certPromise,
|
|
100
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Timeout after ${options.timeout ?? DEFAULT_TIMEOUT_S}s`)), timeoutMs)),
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
abortController.abort();
|
|
105
|
+
await server.finished;
|
|
106
|
+
return Result.Err(err instanceof Error ? err.message : "Login failed");
|
|
107
|
+
}
|
|
108
|
+
// Step 6: Shut down server immediately
|
|
109
|
+
server.close();
|
|
110
|
+
await server.finished;
|
|
111
|
+
// Step 7: Validate and store certificate
|
|
112
|
+
output.stdout("Certificate received. Storing...\n");
|
|
113
|
+
try {
|
|
114
|
+
const decoded = decodeJwt(receivedCert);
|
|
115
|
+
const certPayload = CertificatePayloadSchema.parse(decoded);
|
|
116
|
+
const jwkPrivate = await deviceIdKey.exportPrivateJWK();
|
|
117
|
+
await keyBag.setDeviceId(jwkPrivate, {
|
|
118
|
+
certificateJWT: receivedCert,
|
|
119
|
+
certificatePayload: certPayload,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
return Result.Err(`Failed to store certificate: ${err instanceof Error ? err.message : err}`);
|
|
124
|
+
}
|
|
125
|
+
output.stdout(`Login successful! Device fingerprint: ${fingerprint}\n`);
|
|
126
|
+
return Result.Ok(undefined);
|
|
127
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
export interface ResolvedTarget {
|
|
3
|
+
readonly handle: string;
|
|
4
|
+
readonly app: string;
|
|
5
|
+
readonly group: string;
|
|
6
|
+
readonly full: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ResolveTargetContext {
|
|
9
|
+
readonly app: string;
|
|
10
|
+
readonly handle: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function resolveTarget(ctx: ResolveTargetContext, input?: string): Result<ResolvedTarget>;
|
|
13
|
+
//# sourceMappingURL=resolve-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-target.d.ts","sourceRoot":"","sources":["../../src/commands/resolve-target.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,oBAAoB,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,cAAc,CAAC,CAgD/F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
export function resolveTarget(ctx, input) {
|
|
3
|
+
const { app, handle } = ctx;
|
|
4
|
+
if (input === undefined) {
|
|
5
|
+
return Result.Ok({ handle, app, group: "default", full: `${handle}/${app}/default` });
|
|
6
|
+
}
|
|
7
|
+
if (input === "") {
|
|
8
|
+
return Result.Err("Target must not be empty");
|
|
9
|
+
}
|
|
10
|
+
if (input.startsWith("/") || input.endsWith("/")) {
|
|
11
|
+
return Result.Err(`Invalid target "${input}": must not start or end with /`);
|
|
12
|
+
}
|
|
13
|
+
const slashes = input.split("/").length - 1;
|
|
14
|
+
if (slashes === 0) {
|
|
15
|
+
return Result.Ok({ handle, app, group: input, full: `${handle}/${app}/${input}` });
|
|
16
|
+
}
|
|
17
|
+
if (slashes === 1) {
|
|
18
|
+
const [targetApp, targetGroup] = input.split("/");
|
|
19
|
+
if (targetApp === "" || targetGroup === "") {
|
|
20
|
+
return Result.Err(`Invalid target "${input}": app and group must both be non-empty`);
|
|
21
|
+
}
|
|
22
|
+
return Result.Ok({
|
|
23
|
+
handle,
|
|
24
|
+
app: targetApp,
|
|
25
|
+
group: targetGroup,
|
|
26
|
+
full: `${handle}/${targetApp}/${targetGroup}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (slashes === 2) {
|
|
30
|
+
const [targetHandle, targetApp, targetGroup] = input.split("/");
|
|
31
|
+
if (targetHandle === "" || targetApp === "" || targetGroup === "") {
|
|
32
|
+
return Result.Err(`Invalid target "${input}": handle, app, and group must all be non-empty`);
|
|
33
|
+
}
|
|
34
|
+
return Result.Ok({
|
|
35
|
+
handle: targetHandle,
|
|
36
|
+
app: targetApp,
|
|
37
|
+
group: targetGroup,
|
|
38
|
+
full: input,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return Result.Err(`Invalid target "${input}": expected "group", "app/group", or "handle/app/group"`);
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import { type VibesDiyApiParam } from "@vibes.diy/api-impl";
|
|
3
|
+
import type { DashAuthType, VibesDiyApiIface } from "@vibes.diy/api-types";
|
|
4
|
+
export interface CliVibesApiOptions {
|
|
5
|
+
readonly apiUrl?: string;
|
|
6
|
+
readonly getToken?: () => Promise<Result<DashAuthType>>;
|
|
7
|
+
readonly timeoutMs?: number;
|
|
8
|
+
readonly fetch?: VibesDiyApiParam["fetch"];
|
|
9
|
+
readonly ws?: WebSocket;
|
|
10
|
+
}
|
|
11
|
+
export declare function getCliVibesApiUrl(env?: Readonly<Record<string, string | undefined>>): string;
|
|
12
|
+
export declare function getCliDashAuth(): Promise<Result<DashAuthType>>;
|
|
13
|
+
export declare function toCliVibesApiParam(options?: CliVibesApiOptions): VibesDiyApiParam;
|
|
14
|
+
export declare function createCliVibesApi(options?: CliVibesApiOptions): VibesDiyApiIface;
|
|
15
|
+
//# sourceMappingURL=vibes-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vibes-api.d.ts","sourceRoot":"","sources":["../../src/commands/vibes-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAS3E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC;CACzB;AAED,wBAAgB,iBAAiB,CAAC,GAAG,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAc,GAAG,MAAM,CAQxG;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAyBpE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,kBAAuB,GAAG,gBAAgB,CAQrF;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB,GAAG,gBAAgB,CAEpF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Result } from "@adviser/cement";
|
|
2
|
+
import { VibeDiyApi } from "@vibes.diy/api-impl";
|
|
3
|
+
import { DeviceIdKey, DeviceIdSignMsg } from "@fireproof/core-device-id";
|
|
4
|
+
import { getKeyBag } from "@fireproof/core-keybag";
|
|
5
|
+
import { ensureSuperThis } from "@fireproof/core-runtime";
|
|
6
|
+
import { env as processEnv } from "node:process";
|
|
7
|
+
const DEFAULT_CLI_VIBES_API_URL = "wss://api.vibes.diy/v1/ws";
|
|
8
|
+
const VIBES_API_URL_ENV = "VIBES_DIY_API_URL";
|
|
9
|
+
export function getCliVibesApiUrl(env = processEnv) {
|
|
10
|
+
const envUrl = env[VIBES_API_URL_ENV];
|
|
11
|
+
switch (true) {
|
|
12
|
+
case typeof envUrl === "string" && envUrl.length > 0:
|
|
13
|
+
return envUrl;
|
|
14
|
+
default:
|
|
15
|
+
return DEFAULT_CLI_VIBES_API_URL;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function getCliDashAuth() {
|
|
19
|
+
const sthis = ensureSuperThis();
|
|
20
|
+
const keyBag = await getKeyBag(sthis);
|
|
21
|
+
const existing = await keyBag.getDeviceId();
|
|
22
|
+
if (existing.deviceId.IsNone() || existing.cert.IsNone()) {
|
|
23
|
+
return Result.Err("Not logged in. Run: use-vibes login");
|
|
24
|
+
}
|
|
25
|
+
const jwk = existing.deviceId.unwrap();
|
|
26
|
+
const createResult = await DeviceIdKey.createFromJWK(jwk);
|
|
27
|
+
if (createResult.isErr()) {
|
|
28
|
+
return Result.Err(`Failed to load device key: ${createResult.Err()}`);
|
|
29
|
+
}
|
|
30
|
+
const deviceIdKey = createResult.Ok();
|
|
31
|
+
const cert = existing.cert.unwrap();
|
|
32
|
+
if (!cert) {
|
|
33
|
+
return Result.Err("Not logged in. Run: use-vibes login");
|
|
34
|
+
}
|
|
35
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
36
|
+
const signer = new DeviceIdSignMsg(sthis.txt.base64, deviceIdKey, cert.certificatePayload);
|
|
37
|
+
const token = await signer.sign({ deviceId: fingerprint, seq: 0 });
|
|
38
|
+
return Result.Ok({ type: "device-id", token });
|
|
39
|
+
}
|
|
40
|
+
export function toCliVibesApiParam(options = {}) {
|
|
41
|
+
return {
|
|
42
|
+
apiUrl: options.apiUrl ?? getCliVibesApiUrl(),
|
|
43
|
+
getToken: options.getToken ?? getCliDashAuth,
|
|
44
|
+
timeoutMs: options.timeoutMs,
|
|
45
|
+
fetch: options.fetch,
|
|
46
|
+
ws: options.ws,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function createCliVibesApi(options = {}) {
|
|
50
|
+
return new VibeDiyApi(toCliVibesApiParam(options));
|
|
51
|
+
}
|
package/esm/commands/whoami.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
|
-
|
|
2
|
+
import type { CliOutput } from "./cli-output.js";
|
|
3
|
+
import type { ReqListUserSlugAppSlug, ResListUserSlugAppSlug } from "@vibes.diy/api-types";
|
|
4
|
+
export interface WhoamiListResultLike {
|
|
5
|
+
isErr(): boolean;
|
|
6
|
+
Err(): unknown;
|
|
7
|
+
Ok(): ResListUserSlugAppSlug;
|
|
8
|
+
}
|
|
9
|
+
export interface WhoamiApi {
|
|
10
|
+
listUserSlugAppSlug(req: Omit<ReqListUserSlugAppSlug, "type" | "auth">): Promise<WhoamiListResultLike>;
|
|
11
|
+
}
|
|
12
|
+
export interface WhoamiDeviceInfo {
|
|
13
|
+
readonly fingerprint: string;
|
|
14
|
+
readonly certExpiry: Date | undefined;
|
|
15
|
+
}
|
|
16
|
+
export interface WhoamiDeps {
|
|
17
|
+
readonly api?: WhoamiApi;
|
|
18
|
+
readonly deviceInfo?: WhoamiDeviceInfo;
|
|
19
|
+
}
|
|
20
|
+
export declare function runWhoami(output: CliOutput, deps?: WhoamiDeps): Promise<Result<void>>;
|
|
3
21
|
//# sourceMappingURL=whoami.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAKzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,sBAAsB,EAAE,sBAAsB,EAAiB,MAAM,sBAAsB,CAAC;AAG1G,MAAM,WAAW,oBAAoB;IACnC,KAAK,IAAI,OAAO,CAAC;IACjB,GAAG,IAAI,OAAO,CAAC;IACf,EAAE,IAAI,sBAAsB,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACxG;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,IAAI,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACxC;AAqCD,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CA4B/F"}
|
package/esm/commands/whoami.js
CHANGED
|
@@ -1,4 +1,102 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { DeviceIdKey } from "@fireproof/core-device-id";
|
|
3
|
+
import { getKeyBag } from "@fireproof/core-keybag";
|
|
4
|
+
import { ensureSuperThis } from "@fireproof/core-runtime";
|
|
5
|
+
import { decodeJwt } from "jose";
|
|
6
|
+
import { createCliVibesApi, getCliDashAuth } from "./vibes-api.js";
|
|
7
|
+
function hasCode(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && "code" in value && typeof Reflect.get(value, "code") === "string";
|
|
9
|
+
}
|
|
10
|
+
async function loadDeviceInfo() {
|
|
11
|
+
const sthis = ensureSuperThis();
|
|
12
|
+
const keyBag = await getKeyBag(sthis);
|
|
13
|
+
const existing = await keyBag.getDeviceId();
|
|
14
|
+
if (existing.deviceId.IsNone()) {
|
|
15
|
+
return Result.Err("No device identity. Run: use-vibes login");
|
|
16
|
+
}
|
|
17
|
+
if (existing.cert.IsNone()) {
|
|
18
|
+
return Result.Err("Not registered. Run: use-vibes login");
|
|
19
|
+
}
|
|
20
|
+
const jwk = existing.deviceId.unwrap();
|
|
21
|
+
const createResult = await DeviceIdKey.createFromJWK(jwk);
|
|
22
|
+
if (createResult.isErr()) {
|
|
23
|
+
return Result.Err(`Failed to load device key: ${createResult.Err()}`);
|
|
24
|
+
}
|
|
25
|
+
const deviceIdKey = createResult.Ok();
|
|
26
|
+
const fingerprint = await deviceIdKey.fingerPrint();
|
|
27
|
+
const cert = existing.cert.unwrap();
|
|
28
|
+
if (!cert) {
|
|
29
|
+
return Result.Err("Not registered. Run: use-vibes login");
|
|
30
|
+
}
|
|
31
|
+
const decoded = decodeJwt(cert.certificateJWT);
|
|
32
|
+
const certExpiry = decoded.exp ? new Date(decoded.exp * 1000) : undefined;
|
|
33
|
+
return Result.Ok({ fingerprint, certExpiry });
|
|
34
|
+
}
|
|
35
|
+
export async function runWhoami(output, deps = {}) {
|
|
36
|
+
let deviceInfo;
|
|
37
|
+
if (deps.deviceInfo) {
|
|
38
|
+
deviceInfo = deps.deviceInfo;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
const rDevice = await loadDeviceInfo();
|
|
42
|
+
if (rDevice.isErr()) {
|
|
43
|
+
return Result.Err(rDevice.Err());
|
|
44
|
+
}
|
|
45
|
+
deviceInfo = rDevice.Ok();
|
|
46
|
+
}
|
|
47
|
+
// Fetch handles from API (non-fatal on failure)
|
|
48
|
+
await fetchAndPrintHandles(output, deps);
|
|
49
|
+
output.stdout(`Device: ${deviceInfo.fingerprint}\n`);
|
|
50
|
+
if (deviceInfo.certExpiry) {
|
|
51
|
+
const now = new Date();
|
|
52
|
+
if (deviceInfo.certExpiry < now) {
|
|
53
|
+
output.stdout(`Certificate: expired ${deviceInfo.certExpiry.toISOString()}\n`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
output.stdout(`Certificate: valid until ${deviceInfo.certExpiry.toISOString()}\n`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return Result.Ok(undefined);
|
|
60
|
+
}
|
|
61
|
+
async function fetchAndPrintHandles(output, deps) {
|
|
62
|
+
try {
|
|
63
|
+
const api = await resolveApi(deps);
|
|
64
|
+
if (!api) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const result = await api.listUserSlugAppSlug({});
|
|
68
|
+
if (result.isErr()) {
|
|
69
|
+
const err = result.Err();
|
|
70
|
+
if (hasCode(err) && err.code === "require-login") {
|
|
71
|
+
output.stderr("Session expired — run: use-vibes login\n");
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
output.stderr("Could not reach API — handle info unavailable\n");
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const res = result.Ok();
|
|
79
|
+
if (res.items.length === 0) {
|
|
80
|
+
output.stdout("No handles linked\n");
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
for (const item of res.items) {
|
|
84
|
+
output.stdout(`Handle: @${item.userSlug}\n`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
output.stderr("Could not reach API — handle info unavailable\n");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async function resolveApi(deps) {
|
|
92
|
+
if (deps.api) {
|
|
93
|
+
return deps.api;
|
|
94
|
+
}
|
|
95
|
+
const rAuth = await getCliDashAuth();
|
|
96
|
+
if (rAuth.isErr()) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
return createCliVibesApi({
|
|
100
|
+
getToken: () => Promise.resolve(rAuth),
|
|
101
|
+
});
|
|
4
102
|
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CliRuntime } from "./cli/executable.js";
|
|
2
|
+
export type { CliRuntime } from "./cli/executable.js";
|
|
3
|
+
export type { LoginPlatform } from "./commands/login.js";
|
|
4
|
+
export declare function dispatch(cliArgs: readonly string[], runtime: CliRuntime): Promise<void>;
|
|
5
|
+
//# sourceMappingURL=dispatcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAQzE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAazD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC7F"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { loginExec } from "./cli/exec/login.js";
|
|
2
|
+
import { whoamiExec } from "./cli/exec/whoami.js";
|
|
3
|
+
import { skillsExec } from "./cli/exec/skills.js";
|
|
4
|
+
import { systemExec } from "./cli/exec/system.js";
|
|
5
|
+
import { infoExec } from "./cli/exec/info.js";
|
|
6
|
+
import { handleRegisterExec } from "./cli/exec/handle-register.js";
|
|
7
|
+
const commands = [loginExec, whoamiExec, skillsExec, systemExec, infoExec, handleRegisterExec];
|
|
8
|
+
function printHelp(output) {
|
|
9
|
+
output.stdout("use-vibes — Build and deploy React + Fireproof apps\n\n");
|
|
10
|
+
output.stdout("Commands:\n");
|
|
11
|
+
for (const cmd of commands) {
|
|
12
|
+
output.stdout(` ${cmd.name.padEnd(18)}${cmd.description}\n`);
|
|
13
|
+
}
|
|
14
|
+
output.stdout("\nRun: use-vibes <command> --help\n");
|
|
15
|
+
}
|
|
16
|
+
export async function dispatch(cliArgs, runtime) {
|
|
17
|
+
if (cliArgs.length === 0 || cliArgs[0] === "help" || cliArgs[0] === "-h" || cliArgs[0] === "--help") {
|
|
18
|
+
printHelp(runtime.output);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const token = cliArgs[0];
|
|
22
|
+
// Handle nested "handle register" command
|
|
23
|
+
if (token === "handle") {
|
|
24
|
+
if (cliArgs.length < 2 || cliArgs[1] === "--help" || cliArgs[1] === "-h") {
|
|
25
|
+
runtime.output.stdout("use-vibes handle — Manage handles\n\n");
|
|
26
|
+
runtime.output.stdout("Commands:\n");
|
|
27
|
+
runtime.output.stdout(` register ${handleRegisterExec.description}\n`);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (cliArgs[1] === "register") {
|
|
31
|
+
const code = await handleRegisterExec.run([...cliArgs.slice(2)], runtime);
|
|
32
|
+
runtime.setExitCode(code);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
runtime.output.stderr(`Unknown handle subcommand: ${cliArgs[1]}\n`);
|
|
36
|
+
runtime.setExitCode(1);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const exec = commands.find((c) => c.name === token);
|
|
40
|
+
if (!exec) {
|
|
41
|
+
runtime.output.stderr(`Unknown command: ${token}\n`);
|
|
42
|
+
runtime.setExitCode(1);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const code = await exec.run([...cliArgs.slice(1)], runtime);
|
|
46
|
+
runtime.setExitCode(code);
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "use-vibes",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.29-dev-cli",
|
|
4
4
|
"description": "Transform any DOM element into an AI-powered micro-app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@adviser/cement": "^0.5.32",
|
|
32
32
|
"@vibes.diy/prompts": "*",
|
|
33
|
-
"@vibes.diy/use-vibes-base": "*"
|
|
34
|
-
"cmd-ts": "^0.15.0"
|
|
33
|
+
"@vibes.diy/use-vibes-base": "*"
|
|
35
34
|
},
|
|
36
35
|
"peerDependencies": {
|
|
37
36
|
"react": ">=19.1.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"not-implemented.d.ts","sourceRoot":"","sources":["../../src/commands/not-implemented.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAI1F"}
|
package/esm/run-cli.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { CliOutput } from "./commands/cli-output.js";
|
|
2
|
-
export interface CliRuntime {
|
|
3
|
-
readonly output: CliOutput;
|
|
4
|
-
readonly setExitCode: (code: number) => void;
|
|
5
|
-
}
|
|
6
|
-
export declare function runCli(cliArgs: readonly string[], runtime: CliRuntime): Promise<void>;
|
|
7
|
-
//# sourceMappingURL=run-cli.d.ts.map
|
package/esm/run-cli.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"run-cli.d.ts","sourceRoot":"","sources":["../src/run-cli.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AA+FD,wBAAsB,MAAM,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAiB3F"}
|
package/esm/run-cli.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { command, option, run, runSafely, string, subcommands, restPositionals } from "cmd-ts";
|
|
2
|
-
import { runWhoami } from "./commands/whoami.js";
|
|
3
|
-
import { runSkills } from "./commands/skills.js";
|
|
4
|
-
import { runSystem } from "./commands/system.js";
|
|
5
|
-
import { notImplemented } from "./commands/not-implemented.js";
|
|
6
|
-
async function emitResult(runtime, runner) {
|
|
7
|
-
const result = await runner();
|
|
8
|
-
if (result.isErr()) {
|
|
9
|
-
const err = result.Err();
|
|
10
|
-
runtime.output.stderr(typeof err === "string" ? err : String(err));
|
|
11
|
-
runtime.output.stderr("\n");
|
|
12
|
-
runtime.setExitCode(1);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
async function emitGeneratedHelp(runtime, app) {
|
|
16
|
-
const result = await runSafely(app, ["--help"]);
|
|
17
|
-
if (result._tag === "ok") {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
const target = result.error.config.into === "stdout" ? runtime.output.stdout : runtime.output.stderr;
|
|
21
|
-
target(result.error.config.message);
|
|
22
|
-
if (result.error.config.message.endsWith("\n") === false) {
|
|
23
|
-
target("\n");
|
|
24
|
-
}
|
|
25
|
-
if (result.error.config.exitCode !== 0) {
|
|
26
|
-
runtime.setExitCode(result.error.config.exitCode);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function createStubCommand(runtime, name) {
|
|
30
|
-
return command({
|
|
31
|
-
name,
|
|
32
|
-
description: `${name} is not implemented yet`,
|
|
33
|
-
args: {
|
|
34
|
-
_rest: restPositionals({ description: "arguments" }),
|
|
35
|
-
},
|
|
36
|
-
handler: async function handleStub() {
|
|
37
|
-
await emitResult(runtime, notImplemented({ name }));
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function createApp(runtime) {
|
|
42
|
-
const whoamiCmd = command({
|
|
43
|
-
name: "whoami",
|
|
44
|
-
description: "Print logged in user",
|
|
45
|
-
args: {},
|
|
46
|
-
handler: async function handleWhoami() {
|
|
47
|
-
await emitResult(runtime, runWhoami);
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
const skillsCmd = command({
|
|
51
|
-
name: "skills",
|
|
52
|
-
description: "List available skills",
|
|
53
|
-
args: {},
|
|
54
|
-
handler: async function handleSkills() {
|
|
55
|
-
await emitResult(runtime, () => runSkills(runtime.output));
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
const systemCmd = command({
|
|
59
|
-
name: "system",
|
|
60
|
-
description: "Emit system prompt",
|
|
61
|
-
args: {
|
|
62
|
-
skills: option({
|
|
63
|
-
type: string,
|
|
64
|
-
long: "skills",
|
|
65
|
-
description: "Comma-separated skills, e.g. fireproof,d3",
|
|
66
|
-
defaultValue: () => "",
|
|
67
|
-
}),
|
|
68
|
-
},
|
|
69
|
-
handler: async function handleSystem(args) {
|
|
70
|
-
const skillsCsv = args.skills.length > 0 ? args.skills : undefined;
|
|
71
|
-
await emitResult(runtime, () => runSystem({ skillsCsv }, runtime.output));
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
return subcommands({
|
|
75
|
-
name: "use-vibes",
|
|
76
|
-
description: "Build and deploy React + Fireproof apps",
|
|
77
|
-
cmds: {
|
|
78
|
-
whoami: whoamiCmd,
|
|
79
|
-
login: createStubCommand(runtime, "login"),
|
|
80
|
-
dev: createStubCommand(runtime, "dev"),
|
|
81
|
-
live: createStubCommand(runtime, "live"),
|
|
82
|
-
generate: createStubCommand(runtime, "generate"),
|
|
83
|
-
edit: createStubCommand(runtime, "edit"),
|
|
84
|
-
skills: skillsCmd,
|
|
85
|
-
system: systemCmd,
|
|
86
|
-
publish: createStubCommand(runtime, "publish"),
|
|
87
|
-
invite: createStubCommand(runtime, "invite"),
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
export async function runCli(cliArgs, runtime) {
|
|
92
|
-
const app = createApp(runtime);
|
|
93
|
-
switch (true) {
|
|
94
|
-
case cliArgs.length === 0:
|
|
95
|
-
await emitGeneratedHelp(runtime, app);
|
|
96
|
-
break;
|
|
97
|
-
case cliArgs.length === 1 && (cliArgs[0] === "-h" || cliArgs[0] === "--help"):
|
|
98
|
-
await emitGeneratedHelp(runtime, app);
|
|
99
|
-
break;
|
|
100
|
-
case cliArgs.length === 1 && cliArgs[0] === "help":
|
|
101
|
-
await emitGeneratedHelp(runtime, app);
|
|
102
|
-
break;
|
|
103
|
-
default:
|
|
104
|
-
await run(app, [...cliArgs]);
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
}
|