use-vibes 0.19.29-dev-cli → 0.19.30-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 +0 -2
- package/esm/cli/exec/info.js +3 -3
- package/esm/cli/exec/skills.js +1 -1
- package/esm/cli/exec/system.d.ts.map +1 -1
- package/esm/cli/exec/system.js +11 -12
- package/esm/cli/exec/whoami.js +2 -2
- package/esm/cli/executable.d.ts +0 -2
- package/esm/cli/executable.d.ts.map +1 -1
- package/esm/commands/config.d.ts +9 -6
- package/esm/commands/config.d.ts.map +1 -1
- package/esm/commands/config.js +16 -19
- package/esm/commands/resolve-target.js +2 -2
- package/esm/commands/whoami.d.ts +1 -19
- package/esm/commands/whoami.d.ts.map +1 -1
- package/esm/commands/whoami.js +2 -100
- package/esm/dispatcher.d.ts +0 -1
- package/esm/dispatcher.d.ts.map +1 -1
- package/esm/dispatcher.js +1 -20
- package/package.json +1 -1
- package/esm/cli/exec/handle-register.d.ts +0 -3
- package/esm/cli/exec/handle-register.d.ts.map +0 -1
- package/esm/cli/exec/handle-register.js +0 -18
- package/esm/cli/exec/login.d.ts +0 -3
- package/esm/cli/exec/login.d.ts.map +0 -1
- package/esm/cli/exec/login.js +0 -32
- package/esm/commands/handle-register.d.ts +0 -19
- package/esm/commands/handle-register.d.ts.map +0 -1
- package/esm/commands/handle-register.js +0 -65
- package/esm/commands/login-platform-node.d.ts +0 -3
- package/esm/commands/login-platform-node.d.ts.map +0 -1
- package/esm/commands/login-platform-node.js +0 -56
- package/esm/commands/login.d.ts +0 -22
- package/esm/commands/login.d.ts.map +0 -1
- package/esm/commands/login.js +0 -127
- package/esm/commands/vibes-api.d.ts +0 -15
- package/esm/commands/vibes-api.d.ts.map +0 -1
- package/esm/commands/vibes-api.js +0 -51
package/esm/bin.js
CHANGED
|
@@ -2,12 +2,10 @@
|
|
|
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 { nodeLoginPlatform } from "./commands/login-platform-node.js";
|
|
6
5
|
import { dispatch } from "./dispatcher.js";
|
|
7
6
|
await dispatch(process.argv.slice(2), {
|
|
8
7
|
output: defaultCliOutput,
|
|
9
8
|
setExitCode(code) {
|
|
10
9
|
process.exitCode = code;
|
|
11
10
|
},
|
|
12
|
-
loginPlatform: nodeLoginPlatform,
|
|
13
11
|
});
|
package/esm/cli/exec/info.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { runInfo } from "../../commands/info.js";
|
|
2
2
|
export const infoExec = {
|
|
3
3
|
name: "info",
|
|
4
|
-
description: "Show
|
|
4
|
+
description: "Show project info from vibes.json",
|
|
5
5
|
async run(argv, runtime) {
|
|
6
6
|
if (argv.length > 1) {
|
|
7
|
-
runtime.output.stderr("info accepts at most one target
|
|
7
|
+
runtime.output.stderr("info accepts at most one argument (target)\n");
|
|
8
8
|
return 1;
|
|
9
9
|
}
|
|
10
|
-
const target = argv
|
|
10
|
+
const target = argv[0];
|
|
11
11
|
const result = await runInfo({ target }, runtime.output);
|
|
12
12
|
if (result.isErr()) {
|
|
13
13
|
runtime.output.stderr(String(result.Err()) + "\n");
|
package/esm/cli/exec/skills.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { runSkills } from "../../commands/skills.js";
|
|
2
2
|
export const skillsExec = {
|
|
3
3
|
name: "skills",
|
|
4
|
-
description: "List available
|
|
4
|
+
description: "List available skill libraries",
|
|
5
5
|
async run(_argv, runtime) {
|
|
6
6
|
const result = await runSkills(runtime.output);
|
|
7
7
|
if (result.isErr()) {
|
|
@@ -1 +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;
|
|
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;AAY1D,eAAO,MAAM,UAAU,EAAE,iBAYxB,CAAC"}
|
package/esm/cli/exec/system.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { runSystem } from "../../commands/system.js";
|
|
2
|
+
function parseArgs(argv) {
|
|
3
|
+
for (let i = 0; i < argv.length; i++) {
|
|
4
|
+
if (argv[i] === "--skills" && i + 1 < argv.length) {
|
|
5
|
+
return { skillsCsv: argv[i + 1] };
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
2
10
|
export const systemExec = {
|
|
3
11
|
name: "system",
|
|
4
|
-
description: "
|
|
12
|
+
description: "Output assembled system prompt",
|
|
5
13
|
async run(argv, runtime) {
|
|
6
|
-
|
|
7
|
-
|
|
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);
|
|
14
|
+
const args = parseArgs(argv);
|
|
15
|
+
const result = await runSystem(args, runtime.output);
|
|
17
16
|
if (result.isErr()) {
|
|
18
17
|
runtime.output.stderr(String(result.Err()) + "\n");
|
|
19
18
|
return 1;
|
package/esm/cli/exec/whoami.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { runWhoami } from "../../commands/whoami.js";
|
|
2
2
|
export const whoamiExec = {
|
|
3
3
|
name: "whoami",
|
|
4
|
-
description: "Print
|
|
4
|
+
description: "Print logged-in user",
|
|
5
5
|
async run(_argv, runtime) {
|
|
6
|
-
const result = await runWhoami(
|
|
6
|
+
const result = await runWhoami();
|
|
7
7
|
if (result.isErr()) {
|
|
8
8
|
runtime.output.stderr(String(result.Err()) + "\n");
|
|
9
9
|
return 1;
|
package/esm/cli/executable.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { CliOutput } from "../commands/cli-output.js";
|
|
2
|
-
import type { LoginPlatform } from "../commands/login.js";
|
|
3
2
|
export interface CliRuntime {
|
|
4
3
|
readonly output: CliOutput;
|
|
5
4
|
readonly setExitCode: (code: number) => void;
|
|
6
|
-
readonly loginPlatform: LoginPlatform;
|
|
7
5
|
}
|
|
8
6
|
export interface CommandExecutable {
|
|
9
7
|
readonly name: string;
|
|
@@ -1 +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;
|
|
1
|
+
{"version":3,"file":"executable.d.ts","sourceRoot":"","sources":["../../src/cli/executable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;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"}
|
package/esm/commands/config.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
|
-
|
|
3
|
-
app: string;
|
|
4
|
-
targets?: Record<string,
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
export interface VibesConfig {
|
|
3
|
+
readonly app: string;
|
|
4
|
+
readonly targets?: Record<string, {
|
|
5
|
+
fs?: {
|
|
6
|
+
id: string;
|
|
7
|
+
ts: string;
|
|
8
|
+
}[];
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
7
11
|
export interface FoundConfig {
|
|
8
12
|
readonly path: string;
|
|
9
13
|
readonly config: VibesConfig;
|
|
10
14
|
}
|
|
11
15
|
export declare function findVibesJson(startDir: string): Promise<Result<FoundConfig>>;
|
|
12
|
-
export {};
|
|
13
16
|
//# sourceMappingURL=config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,EAAE,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC,CAAC;CAC1E;AAED,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,CA4BlF"}
|
package/esm/commands/config.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import { join, dirname } from "node:path";
|
|
3
|
-
import { Result
|
|
4
|
-
import { type } from "arktype";
|
|
5
|
-
const VibesJsonType = type({
|
|
6
|
-
app: "string > 0",
|
|
7
|
-
"targets?": "Record<string, unknown>",
|
|
8
|
-
});
|
|
3
|
+
import { Result } from "@adviser/cement";
|
|
9
4
|
export async function findVibesJson(startDir) {
|
|
10
5
|
let dir = startDir;
|
|
11
6
|
for (;;) {
|
|
12
7
|
const candidate = join(dir, "vibes.json");
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
if (typeof
|
|
8
|
+
try {
|
|
9
|
+
const raw = await readFile(candidate, "utf-8");
|
|
10
|
+
const parsed = JSON.parse(raw);
|
|
11
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
12
|
+
return Result.Err(`Invalid vibes.json at ${candidate}: expected an object`);
|
|
13
|
+
}
|
|
14
|
+
const obj = parsed;
|
|
15
|
+
if (typeof obj.app !== "string" || obj.app === "") {
|
|
16
|
+
return Result.Err(`Invalid vibes.json at ${candidate}: "app" must be a non-empty string`);
|
|
17
|
+
}
|
|
18
|
+
return Result.Ok({ path: candidate, config: obj });
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const code = err.code;
|
|
22
|
+
if (code === "ENOENT") {
|
|
17
23
|
const parent = dirname(dir);
|
|
18
24
|
if (parent === dir) {
|
|
19
25
|
return Result.Err("No vibes.json found (searched up to filesystem root)");
|
|
@@ -23,14 +29,5 @@ export async function findVibesJson(startDir) {
|
|
|
23
29
|
}
|
|
24
30
|
return Result.Err(`Error reading ${candidate}: ${err}`);
|
|
25
31
|
}
|
|
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
32
|
}
|
|
36
33
|
}
|
|
@@ -16,7 +16,7 @@ export function resolveTarget(ctx, input) {
|
|
|
16
16
|
}
|
|
17
17
|
if (slashes === 1) {
|
|
18
18
|
const [targetApp, targetGroup] = input.split("/");
|
|
19
|
-
if (targetApp
|
|
19
|
+
if (!targetApp || !targetGroup) {
|
|
20
20
|
return Result.Err(`Invalid target "${input}": app and group must both be non-empty`);
|
|
21
21
|
}
|
|
22
22
|
return Result.Ok({
|
|
@@ -28,7 +28,7 @@ export function resolveTarget(ctx, input) {
|
|
|
28
28
|
}
|
|
29
29
|
if (slashes === 2) {
|
|
30
30
|
const [targetHandle, targetApp, targetGroup] = input.split("/");
|
|
31
|
-
if (targetHandle
|
|
31
|
+
if (!targetHandle || !targetApp || !targetGroup) {
|
|
32
32
|
return Result.Err(`Invalid target "${input}": handle, app, and group must all be non-empty`);
|
|
33
33
|
}
|
|
34
34
|
return Result.Ok({
|
package/esm/commands/whoami.d.ts
CHANGED
|
@@ -1,21 +1,3 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
|
-
|
|
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>>;
|
|
2
|
+
export declare function runWhoami(): Promise<Result<void>>;
|
|
21
3
|
//# 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;AAEzC,wBAAgB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAEjD"}
|
package/esm/commands/whoami.js
CHANGED
|
@@ -1,102 +1,4 @@
|
|
|
1
1
|
import { Result } from "@adviser/cement";
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
});
|
|
2
|
+
export function runWhoami() {
|
|
3
|
+
return Promise.resolve(Result.Err("Not logged in. Run: use-vibes login"));
|
|
102
4
|
}
|
package/esm/dispatcher.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { CliRuntime } from "./cli/executable.js";
|
|
2
2
|
export type { CliRuntime } from "./cli/executable.js";
|
|
3
|
-
export type { LoginPlatform } from "./commands/login.js";
|
|
4
3
|
export declare function dispatch(cliArgs: readonly string[], runtime: CliRuntime): Promise<void>;
|
|
5
4
|
//# sourceMappingURL=dispatcher.d.ts.map
|
package/esm/dispatcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../src/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAMzE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAatD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB7F"}
|
package/esm/dispatcher.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { loginExec } from "./cli/exec/login.js";
|
|
2
1
|
import { whoamiExec } from "./cli/exec/whoami.js";
|
|
3
2
|
import { skillsExec } from "./cli/exec/skills.js";
|
|
4
3
|
import { systemExec } from "./cli/exec/system.js";
|
|
5
4
|
import { infoExec } from "./cli/exec/info.js";
|
|
6
|
-
|
|
7
|
-
const commands = [loginExec, whoamiExec, skillsExec, systemExec, infoExec, handleRegisterExec];
|
|
5
|
+
const commands = [whoamiExec, skillsExec, systemExec, infoExec];
|
|
8
6
|
function printHelp(output) {
|
|
9
7
|
output.stdout("use-vibes — Build and deploy React + Fireproof apps\n\n");
|
|
10
8
|
output.stdout("Commands:\n");
|
|
@@ -19,23 +17,6 @@ export async function dispatch(cliArgs, runtime) {
|
|
|
19
17
|
return;
|
|
20
18
|
}
|
|
21
19
|
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
20
|
const exec = commands.find((c) => c.name === token);
|
|
40
21
|
if (!exec) {
|
|
41
22
|
runtime.output.stderr(`Unknown command: ${token}\n`);
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
};
|
package/esm/cli/exec/login.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/esm/cli/exec/login.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,19 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,65 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,56 +0,0 @@
|
|
|
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
|
-
};
|
package/esm/commands/login.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/esm/commands/login.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
|
@@ -1,51 +0,0 @@
|
|
|
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
|
-
}
|