@storyclaw/talenthub 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +4 -0
- package/dist/commands/agent-publish.d.ts +1 -1
- package/dist/commands/agent-publish.js +4 -2
- package/dist/commands/agent-unpublish.js +2 -1
- package/dist/lib/auth.js +4 -2
- package/dist/lib/fetch.d.ts +9 -0
- package/dist/lib/fetch.js +29 -0
- package/dist/lib/registry.js +4 -3
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import dns from "node:dns";
|
|
3
|
+
// IPv6 is unreachable on many networks (especially behind NAT/China);
|
|
4
|
+
// try IPv4 first to avoid EHOSTUNREACH delays on every fetch.
|
|
5
|
+
dns.setDefaultResultOrder("ipv4first");
|
|
2
6
|
import { Command } from "commander";
|
|
3
7
|
import { agentInstall } from "./commands/agent-install.js";
|
|
4
8
|
import { agentList } from "./commands/agent-list.js";
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import readline from "node:readline";
|
|
4
4
|
import { readAuth, getRegistryBaseUrl } from "../lib/auth.js";
|
|
5
5
|
import { findAgentEntry, readConfig } from "../lib/config.js";
|
|
6
|
+
import { fetchRetry } from "../lib/fetch.js";
|
|
6
7
|
import { resolveWorkspaceDir } from "../lib/paths.js";
|
|
7
8
|
function prompt(question) {
|
|
8
9
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -51,7 +52,7 @@ function readAgentDir(dir) {
|
|
|
51
52
|
}
|
|
52
53
|
return { manifest, files };
|
|
53
54
|
}
|
|
54
|
-
export async function agentPublish(name, opts) {
|
|
55
|
+
export async function agentPublish(name, opts = {}) {
|
|
55
56
|
const auth = readAuth();
|
|
56
57
|
if (!auth) {
|
|
57
58
|
console.error("Not logged in. Run \"talenthub login\" first.");
|
|
@@ -104,7 +105,8 @@ export async function agentPublish(name, opts) {
|
|
|
104
105
|
};
|
|
105
106
|
console.log(`\nPublishing ${payload.emoji || ""} ${payload.name} v${finalVersion}...`);
|
|
106
107
|
const base = getRegistryBaseUrl();
|
|
107
|
-
const
|
|
108
|
+
const url = `${base}/api/talenthub/registry/publish`;
|
|
109
|
+
const res = await fetchRetry(url, {
|
|
108
110
|
method: "POST",
|
|
109
111
|
headers: {
|
|
110
112
|
"Content-Type": "application/json",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import readline from "node:readline";
|
|
2
2
|
import { readAuth, getRegistryBaseUrl } from "../lib/auth.js";
|
|
3
|
+
import { fetchRetry } from "../lib/fetch.js";
|
|
3
4
|
function confirm(question) {
|
|
4
5
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5
6
|
return new Promise((resolve) => {
|
|
@@ -21,7 +22,7 @@ export async function agentUnpublish(name) {
|
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
const base = getRegistryBaseUrl();
|
|
24
|
-
const res = await
|
|
25
|
+
const res = await fetchRetry(`${base}/api/talenthub/registry/${name}/unpublish`, {
|
|
25
26
|
method: "POST",
|
|
26
27
|
headers: {
|
|
27
28
|
"Content-Type": "application/json",
|
package/dist/lib/auth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fetchRetry } from "./fetch.js";
|
|
3
4
|
import { resolveStateDir } from "./paths.js";
|
|
4
5
|
function authFilePath() {
|
|
5
6
|
return path.join(resolveStateDir(), "talenthub-auth.json");
|
|
@@ -38,7 +39,7 @@ export function getRegistryBaseUrl() {
|
|
|
38
39
|
}
|
|
39
40
|
export async function requestDeviceCode() {
|
|
40
41
|
const base = getRegistryBaseUrl();
|
|
41
|
-
const res = await
|
|
42
|
+
const res = await fetchRetry(`${base}/api/talenthub/auth/device-code`, {
|
|
42
43
|
method: "POST",
|
|
43
44
|
headers: { "Content-Type": "application/json" },
|
|
44
45
|
});
|
|
@@ -52,10 +53,11 @@ export async function pollForToken(deviceCode, interval, maxWaitMs) {
|
|
|
52
53
|
const deadline = Date.now() + maxWaitMs;
|
|
53
54
|
while (Date.now() < deadline) {
|
|
54
55
|
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
55
|
-
const res = await
|
|
56
|
+
const res = await fetchRetry(`${base}/api/talenthub/auth/token`, {
|
|
56
57
|
method: "POST",
|
|
57
58
|
headers: { "Content-Type": "application/json" },
|
|
58
59
|
body: JSON.stringify({ device_code: deviceCode }),
|
|
60
|
+
retries: 2,
|
|
59
61
|
});
|
|
60
62
|
if (res.ok) {
|
|
61
63
|
return res.json();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fetch wrapper with timeout + retry for transient network errors.
|
|
3
|
+
* Retries only on TypeError (network-level failures like ETIMEDOUT,
|
|
4
|
+
* EHOSTUNREACH, ECONNRESET); HTTP errors are returned as-is.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fetchRetry(url: string, init?: RequestInit & {
|
|
7
|
+
retries?: number;
|
|
8
|
+
timeoutMs?: number;
|
|
9
|
+
}): Promise<Response>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
2
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
3
|
+
/**
|
|
4
|
+
* fetch wrapper with timeout + retry for transient network errors.
|
|
5
|
+
* Retries only on TypeError (network-level failures like ETIMEDOUT,
|
|
6
|
+
* EHOSTUNREACH, ECONNRESET); HTTP errors are returned as-is.
|
|
7
|
+
*/
|
|
8
|
+
export async function fetchRetry(url, init) {
|
|
9
|
+
const maxRetries = init?.retries ?? DEFAULT_MAX_RETRIES;
|
|
10
|
+
const timeoutMs = init?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
11
|
+
let lastError;
|
|
12
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
13
|
+
try {
|
|
14
|
+
return await fetch(url, {
|
|
15
|
+
...init,
|
|
16
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
lastError = err;
|
|
21
|
+
if (attempt < maxRetries) {
|
|
22
|
+
const delay = attempt * 1_000;
|
|
23
|
+
console.warn(` Network error, retrying (${attempt}/${maxRetries}) in ${delay / 1000}s...`);
|
|
24
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
throw lastError;
|
|
29
|
+
}
|
package/dist/lib/registry.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { getRegistryBaseUrl } from "./auth.js";
|
|
2
|
+
import { fetchRetry } from "./fetch.js";
|
|
2
3
|
function apiUrl(path) {
|
|
3
4
|
const base = getRegistryBaseUrl().replace(/\/$/, "");
|
|
4
|
-
return `${base}/api/
|
|
5
|
+
return `${base}/api/talenthub/registry${path}`;
|
|
5
6
|
}
|
|
6
7
|
export async function fetchCatalog() {
|
|
7
8
|
const url = apiUrl("/catalog");
|
|
8
|
-
const res = await
|
|
9
|
+
const res = await fetchRetry(url);
|
|
9
10
|
if (!res.ok) {
|
|
10
11
|
throw new Error(`Failed to fetch catalog: ${res.status} ${res.statusText}`);
|
|
11
12
|
}
|
|
@@ -13,7 +14,7 @@ export async function fetchCatalog() {
|
|
|
13
14
|
}
|
|
14
15
|
export async function fetchManifest(agentId) {
|
|
15
16
|
const url = apiUrl(`/${agentId}`);
|
|
16
|
-
const res = await
|
|
17
|
+
const res = await fetchRetry(url);
|
|
17
18
|
if (!res.ok) {
|
|
18
19
|
throw new Error(`Agent "${agentId}" not found in registry (${res.status})`);
|
|
19
20
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@storyclaw/talenthub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "CLI tool to manage StoryClaw AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,6 @@
|
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"repository": {
|
|
31
31
|
"type": "git",
|
|
32
|
-
"url": "https://github.com/storyclaw-official/
|
|
32
|
+
"url": "https://github.com/storyclaw-official/talenthub"
|
|
33
33
|
}
|
|
34
34
|
}
|