@storyclaw/talenthub 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +5 -1
- package/dist/commands/login.d.ts +3 -1
- package/dist/commands/login.js +26 -2
- package/dist/lib/auth.d.ts +10 -0
- package/dist/lib/auth.js +31 -0
- package/dist/lib/registry.js +10 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -23,7 +23,11 @@ program
|
|
|
23
23
|
.name("talenthub")
|
|
24
24
|
.description("Manage StoryClaw AI agents")
|
|
25
25
|
.version(pkg.version);
|
|
26
|
-
program
|
|
26
|
+
program
|
|
27
|
+
.command("login")
|
|
28
|
+
.description("Authenticate with StoryClaw")
|
|
29
|
+
.option("-t, --token <token>", "Authenticate directly with an sc_token")
|
|
30
|
+
.action(login);
|
|
27
31
|
program.command("logout").description("Remove stored credentials").action(logout);
|
|
28
32
|
const agent = program.command("agent").description("Agent management commands");
|
|
29
33
|
agent
|
package/dist/commands/login.d.ts
CHANGED
package/dist/commands/login.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
-
import { readAuth, requestDeviceCode, pollForToken, writeAuth } from "../lib/auth.js";
|
|
2
|
+
import { readAuth, requestDeviceCode, pollForToken, exchangeToken, verifyToken, writeAuth } from "../lib/auth.js";
|
|
3
3
|
function openUrl(url) {
|
|
4
4
|
try {
|
|
5
5
|
const cmd = process.platform === "darwin"
|
|
@@ -13,7 +13,31 @@ function openUrl(url) {
|
|
|
13
13
|
// Browser open is best-effort
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
-
export async function login() {
|
|
16
|
+
export async function login(options) {
|
|
17
|
+
if (options.token) {
|
|
18
|
+
try {
|
|
19
|
+
if (options.token.startsWith("th_")) {
|
|
20
|
+
console.log("Verifying token...");
|
|
21
|
+
const { user_id } = await verifyToken(options.token);
|
|
22
|
+
writeAuth({ token: options.token, user_id, expires_at: "" });
|
|
23
|
+
console.log(`✓ Logged in successfully.`);
|
|
24
|
+
console.log(` User ID: ${user_id}`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// sc_token from web session — exchange for a CLI token
|
|
28
|
+
console.log("Exchanging token...");
|
|
29
|
+
const { access_token, user_id, expires_at } = await exchangeToken(options.token);
|
|
30
|
+
writeAuth({ token: access_token, user_id, expires_at });
|
|
31
|
+
console.log(`✓ Logged in successfully.`);
|
|
32
|
+
console.log(` User ID: ${user_id}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
console.error(`✗ Login failed: ${err instanceof Error ? err.message : err}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
17
41
|
const existing = readAuth();
|
|
18
42
|
if (existing) {
|
|
19
43
|
console.log("Already logged in.");
|
package/dist/lib/auth.d.ts
CHANGED
|
@@ -20,4 +20,14 @@ export type TokenResponse = {
|
|
|
20
20
|
user_id: string;
|
|
21
21
|
expires_at: string;
|
|
22
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* Exchange an sc_token (web session cookie) for a CLI-compatible th_* token.
|
|
25
|
+
*/
|
|
26
|
+
export declare function exchangeToken(scToken: string): Promise<TokenResponse>;
|
|
27
|
+
/**
|
|
28
|
+
* Verify a th_* CLI token against the registry and return the user_id.
|
|
29
|
+
*/
|
|
30
|
+
export declare function verifyToken(token: string): Promise<{
|
|
31
|
+
user_id: string;
|
|
32
|
+
}>;
|
|
23
33
|
export declare function pollForToken(deviceCode: string, interval: number, maxWaitMs: number): Promise<TokenResponse>;
|
package/dist/lib/auth.js
CHANGED
|
@@ -48,6 +48,37 @@ export async function requestDeviceCode() {
|
|
|
48
48
|
}
|
|
49
49
|
return res.json();
|
|
50
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Exchange an sc_token (web session cookie) for a CLI-compatible th_* token.
|
|
53
|
+
*/
|
|
54
|
+
export async function exchangeToken(scToken) {
|
|
55
|
+
const base = getRegistryBaseUrl();
|
|
56
|
+
const res = await fetchRetry(`${base}/api/talenthub/auth/token-exchange`, {
|
|
57
|
+
method: "POST",
|
|
58
|
+
headers: { "Content-Type": "application/json" },
|
|
59
|
+
body: JSON.stringify({ token: scToken }),
|
|
60
|
+
});
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
const body = await res.json().catch(() => ({ error: "unknown" }));
|
|
63
|
+
throw new Error(`Token exchange failed (${res.status}): ${body.error ?? "unknown error"}`);
|
|
64
|
+
}
|
|
65
|
+
return res.json();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Verify a th_* CLI token against the registry and return the user_id.
|
|
69
|
+
*/
|
|
70
|
+
export async function verifyToken(token) {
|
|
71
|
+
const base = getRegistryBaseUrl();
|
|
72
|
+
const res = await fetchRetry(`${base}/api/talenthub/auth/me`, {
|
|
73
|
+
method: "GET",
|
|
74
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
75
|
+
});
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const body = await res.json().catch(() => ({ error: "unknown" }));
|
|
78
|
+
throw new Error(`Token verification failed (${res.status}): ${body.error ?? "unknown error"}`);
|
|
79
|
+
}
|
|
80
|
+
return res.json();
|
|
81
|
+
}
|
|
51
82
|
export async function pollForToken(deviceCode, interval, maxWaitMs) {
|
|
52
83
|
const base = getRegistryBaseUrl();
|
|
53
84
|
const deadline = Date.now() + maxWaitMs;
|
package/dist/lib/registry.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { getRegistryBaseUrl } from "./auth.js";
|
|
1
|
+
import { getRegistryBaseUrl, readAuth } from "./auth.js";
|
|
2
2
|
import { fetchRetry } from "./fetch.js";
|
|
3
3
|
function apiUrl(path) {
|
|
4
4
|
const base = getRegistryBaseUrl().replace(/\/$/, "");
|
|
5
5
|
return `${base}/api/talenthub/registry${path}`;
|
|
6
6
|
}
|
|
7
|
+
function authHeaders() {
|
|
8
|
+
const auth = readAuth();
|
|
9
|
+
if (auth?.token) {
|
|
10
|
+
return { Authorization: `Bearer ${auth.token}` };
|
|
11
|
+
}
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
7
14
|
export async function fetchCatalog() {
|
|
8
15
|
const url = apiUrl("/catalog");
|
|
9
|
-
const res = await fetchRetry(url);
|
|
16
|
+
const res = await fetchRetry(url, { headers: authHeaders() });
|
|
10
17
|
if (!res.ok) {
|
|
11
18
|
throw new Error(`Failed to fetch catalog: ${res.status} ${res.statusText}`);
|
|
12
19
|
}
|
|
@@ -14,7 +21,7 @@ export async function fetchCatalog() {
|
|
|
14
21
|
}
|
|
15
22
|
export async function fetchManifest(agentId) {
|
|
16
23
|
const url = apiUrl(`/${agentId}`);
|
|
17
|
-
const res = await fetchRetry(url);
|
|
24
|
+
const res = await fetchRetry(url, { headers: authHeaders() });
|
|
18
25
|
if (!res.ok) {
|
|
19
26
|
throw new Error(`Agent "${agentId}" not found in registry (${res.status})`);
|
|
20
27
|
}
|