frely-cli 0.3.7 → 0.4.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.
@@ -0,0 +1,166 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { constants } from "node:fs";
3
+ import { lstat, mkdir, open, rename, unlink } from "node:fs/promises";
4
+ import { dirname, join, resolve } from "node:path";
5
+ import { NetworkError } from "./network-errors.js";
6
+ export const hashText = (value) => createHash("sha256").update(value).digest("hex");
7
+ const record = (value) => !!value && typeof value === "object" && !Array.isArray(value);
8
+ const absent = (error) => error.code === "ENOENT";
9
+ /** Reject ancestor symlinks, including dangling links. Never traverse a user-managed redirect. */
10
+ export async function assertSafePath(path) {
11
+ let current = resolve(path);
12
+ while (true) {
13
+ const stat = await lstat(current).catch((error) => { if (absent(error))
14
+ return null; throw error; });
15
+ if (stat?.isSymbolicLink())
16
+ throw new NetworkError("UNSAFE_LOCAL_PATH");
17
+ const parent = dirname(current);
18
+ if (parent === current)
19
+ break;
20
+ current = parent;
21
+ }
22
+ }
23
+ async function readOwnedFile(path, maxBytes = 512 * 1024) {
24
+ await assertSafePath(path);
25
+ let file;
26
+ try {
27
+ file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW);
28
+ }
29
+ catch (error) {
30
+ if (absent(error))
31
+ return null;
32
+ throw new NetworkError("LOCAL_FILE_READ_FAILED");
33
+ }
34
+ try {
35
+ const stat = await file.stat();
36
+ if (!stat.isFile() || stat.size > maxBytes)
37
+ throw new NetworkError("LOCAL_FILE_INVALID");
38
+ return await file.readFile("utf8");
39
+ }
40
+ finally {
41
+ await file.close();
42
+ }
43
+ }
44
+ async function atomicWrite(path, value) {
45
+ await assertSafePath(path);
46
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
47
+ await assertSafePath(path);
48
+ const temporary = `${path}.${randomUUID()}.tmp`;
49
+ const file = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
50
+ try {
51
+ await file.writeFile(value, "utf8");
52
+ await file.sync();
53
+ }
54
+ finally {
55
+ await file.close();
56
+ }
57
+ try {
58
+ await assertSafePath(path);
59
+ await rename(temporary, path);
60
+ }
61
+ finally {
62
+ await unlink(temporary).catch(() => undefined);
63
+ }
64
+ }
65
+ export class NetworkFiles {
66
+ home;
67
+ directory;
68
+ constructor(home) {
69
+ this.home = home;
70
+ this.directory = join(home, ".config", "frely", "network");
71
+ }
72
+ async readConfig() {
73
+ const text = await readOwnedFile(join(this.directory, "config.json"), 4096);
74
+ if (text === null)
75
+ return null;
76
+ let value;
77
+ try {
78
+ value = JSON.parse(text);
79
+ }
80
+ catch {
81
+ throw new NetworkError("LOCAL_CONFIG_INVALID");
82
+ }
83
+ if (!record(value) || value.version !== 1 || typeof value.origin !== "string" ||
84
+ !["chatgpt", "claude-code", "opencode", "generic"].includes(String(value.host)))
85
+ throw new NetworkError("LOCAL_CONFIG_INVALID");
86
+ return { version: 1, origin: value.origin, host: value.host };
87
+ }
88
+ async writeConfig(value) { await atomicWrite(join(this.directory, "config.json"), JSON.stringify(value)); }
89
+ /** Serialize local grant exchange so competing CLI calls cannot consume or delete each other's session. */
90
+ async lock() {
91
+ await assertSafePath(this.directory);
92
+ await mkdir(this.directory, { recursive: true, mode: 0o700 });
93
+ const path = join(this.directory, "command.lock");
94
+ await assertSafePath(path);
95
+ for (let attempt = 0; attempt < 2; attempt++) {
96
+ try {
97
+ const handle = await open(path, "wx", 0o600);
98
+ const value = JSON.stringify({ pid: process.pid, nonce: randomUUID() });
99
+ await handle.writeFile(value);
100
+ await handle.close();
101
+ return async () => {
102
+ // Do not remove a lock replaced by another process.
103
+ if (await readOwnedFile(path, 512) === value)
104
+ await unlink(path).catch(() => undefined);
105
+ };
106
+ }
107
+ catch (error) {
108
+ if (error.code !== "EEXIST")
109
+ throw new NetworkError("LOCAL_LOCK_FAILED");
110
+ const text = await readOwnedFile(path, 512);
111
+ let pid = 0;
112
+ try {
113
+ const item = JSON.parse(text ?? "null");
114
+ if (record(item) && Number.isSafeInteger(item.pid) && Number(item.pid) > 0)
115
+ pid = Number(item.pid);
116
+ }
117
+ catch { /* Unknown lock ownership must not be overwritten. */ }
118
+ if (!pid)
119
+ throw new NetworkError("NETWORK_CLIENT_BUSY");
120
+ let alive = true;
121
+ try {
122
+ process.kill(pid, 0);
123
+ }
124
+ catch (failure) {
125
+ alive = failure.code !== "ESRCH";
126
+ }
127
+ if (alive || attempt > 0)
128
+ throw new NetworkError("NETWORK_CLIENT_BUSY");
129
+ if (await readOwnedFile(path, 512) === text)
130
+ await unlink(path);
131
+ }
132
+ }
133
+ throw new NetworkError("NETWORK_CLIENT_BUSY");
134
+ }
135
+ async installSkill(host, skill) {
136
+ if (host === "chatgpt")
137
+ return { instructionMode: "conversation", installed: false };
138
+ const root = host === "claude-code" ? join(this.home, ".claude", "skills") :
139
+ host === "opencode" ? join(this.home, ".config", "opencode", "skills") : join(this.home, ".agents", "skills");
140
+ const path = join(root, "frely-network", "SKILL.md");
141
+ const metadata = join(root, "frely-network", ".frely-managed.json");
142
+ const text = await readOwnedFile(path);
143
+ const metadataText = await readOwnedFile(metadata, 4096);
144
+ let marker = null;
145
+ if (metadataText !== null) {
146
+ try {
147
+ const value = JSON.parse(metadataText);
148
+ if (record(value))
149
+ marker = value;
150
+ }
151
+ catch { /* Fail closed below. */ }
152
+ if (!marker || marker.version !== 1 || marker.owner !== "frely-network")
153
+ throw new NetworkError("UNMANAGED_SKILL_EXISTS");
154
+ }
155
+ if (text !== null && (!marker || (hashText(text) !== marker.sha256 && hashText(text) !== marker.pendingSha256))) {
156
+ throw new NetworkError("UNMANAGED_SKILL_EXISTS");
157
+ }
158
+ const hash = hashText(skill);
159
+ // A pending hash recovers an interrupted managed-file replacement, not an unowned file.
160
+ await atomicWrite(metadata, JSON.stringify({ version: 1, owner: "frely-network", sha256: text === null ? null : hashText(text), pendingSha256: hash }));
161
+ await atomicWrite(path, skill);
162
+ await atomicWrite(metadata, JSON.stringify({ version: 1, owner: "frely-network", sha256: hash }));
163
+ return { instructionMode: "skill", installed: true };
164
+ }
165
+ }
166
+ //# sourceMappingURL=network-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-files.js","sourceRoot":"","sources":["../src/network-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
2
+ export declare const UUID_PATTERN: RegExp;
3
+ export declare function normalizeNetworkOrigin(input: string, allowLoopback: boolean): string;
4
+ export interface NetworkHttpResponse {
5
+ status: number;
6
+ text: string;
7
+ headers: Headers;
8
+ }
9
+ export declare class NetworkHttp {
10
+ private readonly fetcher;
11
+ private readonly timeoutMs;
12
+ constructor(fetcher: typeof fetch, timeoutMs: number);
13
+ request(origin: string, path: string, options?: {
14
+ body?: unknown;
15
+ token?: string;
16
+ method?: string;
17
+ requestId?: string;
18
+ }): Promise<NetworkHttpResponse>;
19
+ parse(response: NetworkHttpResponse, secrets?: string[]): Record<string, unknown>;
20
+ }
@@ -0,0 +1,92 @@
1
+ import { NetworkError } from "./network-errors.js";
2
+ export const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
3
+ export const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
4
+ export function normalizeNetworkOrigin(input, allowLoopback) {
5
+ let url;
6
+ try {
7
+ url = new URL(input);
8
+ }
9
+ catch {
10
+ throw new NetworkError("NETWORK_ORIGIN_INVALID");
11
+ }
12
+ if ((url.protocol !== "https:" && !(allowLoopback && url.protocol === "http:" && ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname))) ||
13
+ url.username || url.password || url.search || url.hash || url.pathname !== "/")
14
+ throw new NetworkError("NETWORK_ORIGIN_INVALID");
15
+ return url.origin;
16
+ }
17
+ export class NetworkHttp {
18
+ fetcher;
19
+ timeoutMs;
20
+ constructor(fetcher, timeoutMs) {
21
+ this.fetcher = fetcher;
22
+ this.timeoutMs = timeoutMs;
23
+ }
24
+ async request(origin, path, options = {}) {
25
+ const controller = new AbortController();
26
+ let timer;
27
+ const run = async () => {
28
+ const response = await this.fetcher(`${origin}${path}`, {
29
+ method: options.method ?? (options.body === undefined ? "GET" : "POST"), redirect: "error",
30
+ signal: controller.signal,
31
+ headers: { accept: path === "/SKILL.md" ? "text/markdown" : "application/json",
32
+ ...(options.body === undefined ? {} : { "content-type": "application/json" }),
33
+ ...(options.token ? { authorization: `Bearer ${options.token}` } : {}),
34
+ ...(options.requestId ? { "idempotency-key": options.requestId } : {}) },
35
+ ...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }),
36
+ });
37
+ if (response.redirected || (response.status >= 300 && response.status < 400) ||
38
+ (response.url && new URL(response.url).origin !== origin))
39
+ throw new NetworkError("NETWORK_REDIRECT_REJECTED");
40
+ const reader = response.body?.getReader();
41
+ const chunks = [];
42
+ let total = 0;
43
+ if (reader) {
44
+ try {
45
+ while (true) {
46
+ const chunk = await reader.read();
47
+ if (chunk.done)
48
+ break;
49
+ total += chunk.value.byteLength;
50
+ if (total > 512 * 1024) {
51
+ await reader.cancel();
52
+ throw new NetworkError("NETWORK_RESPONSE_TOO_LARGE");
53
+ }
54
+ chunks.push(chunk.value);
55
+ }
56
+ }
57
+ finally {
58
+ reader.releaseLock();
59
+ }
60
+ }
61
+ return { status: response.status, text: new TextDecoder("utf-8", { fatal: true }).decode(Buffer.concat(chunks)), headers: response.headers };
62
+ };
63
+ const timeout = new Promise((_, reject) => {
64
+ timer = setTimeout(() => { controller.abort(); reject(new NetworkError("NETWORK_TIMEOUT")); }, this.timeoutMs);
65
+ });
66
+ try {
67
+ return await Promise.race([run(), timeout]);
68
+ }
69
+ catch (error) {
70
+ throw error instanceof NetworkError ? error : new NetworkError("NETWORK_REQUEST_FAILED");
71
+ }
72
+ finally {
73
+ clearTimeout(timer);
74
+ }
75
+ }
76
+ parse(response, secrets = []) {
77
+ // No authenticated service response may reflect a credential to the host.
78
+ if (secrets.some((secret) => secret.length >= 16 && response.text.includes(secret)))
79
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
80
+ let value;
81
+ try {
82
+ value = JSON.parse(response.text);
83
+ }
84
+ catch {
85
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
86
+ }
87
+ if (!isRecord(value))
88
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
89
+ return value;
90
+ }
91
+ }
92
+ //# sourceMappingURL=network-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-http.js","sourceRoot":"","sources":["../src/network-http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { CredentialStore } from "./credential-store.js";
2
+ import { NetworkHttp } from "./network-http.js";
3
+ import { type NetworkState, type PendingGrant, type StoredNetworkSession, type NetworkSessionView } from "./network-contract.js";
4
+ export type ResolvedNetworkSession = {
5
+ kind: "none";
6
+ reason?: string;
7
+ } | {
8
+ kind: "pending";
9
+ value: PendingGrant;
10
+ } | {
11
+ kind: "ready";
12
+ credential: StoredNetworkSession;
13
+ view: NetworkSessionView;
14
+ };
15
+ /** Keep Network credentials separate from the Frely account and device-relay sessions. */
16
+ export declare class NetworkSessions {
17
+ private readonly origin;
18
+ private readonly store;
19
+ private readonly http;
20
+ private readonly now;
21
+ constructor(origin: string, store: CredentialStore, http: NetworkHttp, now: () => number);
22
+ save(value: Exclude<NetworkState, null>): Promise<void>;
23
+ remove(): Promise<void>;
24
+ read(): Promise<NetworkState>;
25
+ resolve(state: NetworkState): Promise<ResolvedNetworkSession>;
26
+ logout(state: NetworkState): Promise<Record<string, unknown>>;
27
+ }
@@ -0,0 +1,119 @@
1
+ import { NetworkError } from "./network-errors.js";
2
+ import { isRecord, UUID_PATTERN } from "./network-http.js";
3
+ import { ACCESS_TOKEN_PATTERN, NETWORK_SERVICE, parsePendingGrant, parseSessionView, validExpiry, networkServerError } from "./network-contract.js";
4
+ /** Keep Network credentials separate from the Frely account and device-relay sessions. */
5
+ export class NetworkSessions {
6
+ origin;
7
+ store;
8
+ http;
9
+ now;
10
+ constructor(origin, store, http, now) {
11
+ this.origin = origin;
12
+ this.store = store;
13
+ this.http = http;
14
+ this.now = now;
15
+ }
16
+ async save(value) {
17
+ try {
18
+ await this.store.setPassword(NETWORK_SERVICE, this.origin, JSON.stringify(value));
19
+ }
20
+ catch {
21
+ throw new NetworkError("CREDENTIAL_STORE_FAILED");
22
+ }
23
+ }
24
+ async remove() {
25
+ try {
26
+ await this.store.deletePassword(NETWORK_SERVICE, this.origin);
27
+ }
28
+ catch {
29
+ throw new NetworkError("CREDENTIAL_STORE_FAILED");
30
+ }
31
+ }
32
+ async read() {
33
+ let text;
34
+ try {
35
+ text = await this.store.getPassword(NETWORK_SERVICE, this.origin);
36
+ }
37
+ catch {
38
+ throw new NetworkError("CREDENTIAL_STORE_FAILED");
39
+ }
40
+ if (!text)
41
+ return null;
42
+ let value;
43
+ try {
44
+ value = JSON.parse(text);
45
+ }
46
+ catch {
47
+ throw new NetworkError("CREDENTIAL_STATE_INVALID");
48
+ }
49
+ if (!isRecord(value) || value.version !== 1 || !validExpiry(value.expiresAt))
50
+ throw new NetworkError("CREDENTIAL_STATE_INVALID");
51
+ if (value.kind === "pending")
52
+ return parsePendingGrant({ ...value, paymentMode: "platform_demo" }, this.origin);
53
+ if (value.kind !== "session" || typeof value.accessToken !== "string" || !ACCESS_TOKEN_PATTERN.test(value.accessToken) ||
54
+ typeof value.sessionId !== "string" || !UUID_PATTERN.test(value.sessionId))
55
+ throw new NetworkError("CREDENTIAL_STATE_INVALID");
56
+ return { version: 1, kind: "session", accessToken: value.accessToken, expiresAt: value.expiresAt, sessionId: value.sessionId };
57
+ }
58
+ async resolve(state) {
59
+ if (!state)
60
+ return { kind: "none" };
61
+ if (Date.parse(state.expiresAt) <= this.now()) {
62
+ await this.remove();
63
+ return { kind: "none", reason: "AUTHORIZATION_EXPIRED" };
64
+ }
65
+ if (state.kind === "pending") {
66
+ const response = await this.http.request(this.origin, "/api/network/device/token", { body: { deviceCode: state.deviceCode } });
67
+ const value = this.http.parse(response, [state.deviceCode]);
68
+ if (response.status === 202) {
69
+ if (value.status !== "awaiting_wallet" || !Number.isSafeInteger(value.intervalSeconds) || Number(value.intervalSeconds) < 1)
70
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
71
+ return { kind: "pending", value: state };
72
+ }
73
+ if (response.status !== 200) {
74
+ const error = networkServerError(value);
75
+ if (["DEVICE_EXPIRED", "DEVICE_CONSUMED", "DEVICE_REJECTED"].includes(error.code)) {
76
+ await this.remove();
77
+ return { kind: "none", reason: error.code };
78
+ }
79
+ throw error;
80
+ }
81
+ const view = parseSessionView(value);
82
+ if (typeof value.accessToken !== "string" || !ACCESS_TOKEN_PATTERN.test(value.accessToken) || Date.parse(view.expiresAt) <= this.now())
83
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
84
+ state = { version: 1, kind: "session", accessToken: value.accessToken, expiresAt: view.expiresAt, sessionId: view.sessionId };
85
+ await this.save(state);
86
+ }
87
+ const response = await this.http.request(this.origin, "/api/network/session", { token: state.accessToken });
88
+ const value = this.http.parse(response, [state.accessToken]);
89
+ if (response.status === 401) {
90
+ await this.remove();
91
+ return { kind: "none", reason: networkServerError(value).code };
92
+ }
93
+ if (response.status !== 200)
94
+ throw networkServerError(value);
95
+ const view = parseSessionView(value);
96
+ if (view.sessionId !== state.sessionId || Date.parse(view.expiresAt) <= this.now())
97
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
98
+ return { kind: "ready", credential: state, view };
99
+ }
100
+ async logout(state) {
101
+ if (state?.kind === "session") {
102
+ let revoked = false;
103
+ try {
104
+ const response = await this.http.request(this.origin, "/api/network/session", { method: "DELETE", token: state.accessToken });
105
+ const value = this.http.parse(response, [state.accessToken]);
106
+ revoked = response.status === 200 && value.status === "revoked";
107
+ }
108
+ catch { /* Report an unconfirmed revocation as a failure. */ }
109
+ await this.remove();
110
+ if (!revoked)
111
+ throw new NetworkError("REVOCATION_UNCONFIRMED", { remoteRevocation: false,
112
+ recovery: "The local credential was removed. The remote session expires at its original expiry." });
113
+ return { status: "logged_out", remoteRevocation: true, origin: this.origin };
114
+ }
115
+ await this.remove();
116
+ return { status: "logged_out", remoteRevocation: false, origin: this.origin, pendingGrantExpiresAt: state?.expiresAt ?? null };
117
+ }
118
+ }
119
+ //# sourceMappingURL=network-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network-session.js","sourceRoot":"","sources":["../src/network-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { CredentialStore } from "./credential-store.js";
2
+ export { NetworkError, publicNetworkError } from "./network-errors.js";
3
+ export interface NetworkDeps {
4
+ store?: CredentialStore;
5
+ fetch?: typeof fetch;
6
+ home?: string;
7
+ now?: () => number;
8
+ uuid?: () => string;
9
+ timeoutMs?: number;
10
+ allowLoopback?: boolean;
11
+ }
12
+ export declare function runNetwork(args: string[], deps?: NetworkDeps): Promise<Record<string, unknown>>;
@@ -0,0 +1,111 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { homedir } from "node:os";
3
+ import { VERSION } from "./version.js";
4
+ import { NetworkError } from "./network-errors.js";
5
+ import { NetworkFiles } from "./network-files.js";
6
+ import { NetworkHttp, normalizeNetworkOrigin, isRecord, UUID_PATTERN } from "./network-http.js";
7
+ import { NetworkSessions } from "./network-session.js";
8
+ import { DEFAULT_NETWORK_ORIGIN, NETWORK_HOSTS, parseNetworkArgs, parseCapability, parseUseInput, parsePendingGrant, publicPendingGrant, networkServerError } from "./network-contract.js";
9
+ export { NetworkError, publicNetworkError } from "./network-errors.js";
10
+ export async function runNetwork(args, deps = {}) {
11
+ const { command, flags } = parseNetworkArgs(args);
12
+ if (command === "help")
13
+ return { commands: ["network setup [--host chatgpt|claude-code|opencode|generic] [--network <origin>] --json",
14
+ "network status --json", "network find --capability <capability> --json",
15
+ "network use --capability <capability> --input-json <json> [--request-id <uuid>] --json", "network logout --json"] };
16
+ const cap = ["find", "use"].includes(command) ? parseCapability(flags) : undefined;
17
+ const input = command === "use" ? parseUseInput(flags, cap) : undefined;
18
+ const id = command === "use" ? (flags["--request-id"] ?? (deps.uuid ?? randomUUID)()).toLowerCase() : undefined;
19
+ if (id && !UUID_PATTERN.test(id))
20
+ throw new NetworkError("REQUEST_ID_INVALID");
21
+ const task = flags["--task"] ?? (cap ? `Assess ${cap} using the supplied target and source evidence.` : "");
22
+ if (command === "use" && (!task.trim() || task.length > 4000))
23
+ throw new NetworkError("TASK_INVALID");
24
+ if (Number(process.versions.node.split(".")[0]) < 22)
25
+ throw new NetworkError("NODE_VERSION_UNSUPPORTED");
26
+ const files = new NetworkFiles(deps.home ?? homedir());
27
+ let unlock;
28
+ try {
29
+ unlock = await files.lock();
30
+ const config = await files.readConfig();
31
+ const origin = normalizeNetworkOrigin(flags["--network"] ?? config?.origin ?? DEFAULT_NETWORK_ORIGIN, deps.allowLoopback ?? process.env.FRELY_NETWORK_ALLOW_LOOPBACK === "1");
32
+ const host = flags["--host"] ?? config?.host ?? "generic";
33
+ if (!NETWORK_HOSTS.includes(host))
34
+ throw new NetworkError("HOST_UNSUPPORTED");
35
+ const store = deps.store ?? (await import("./credential-store.js")).credentialStore;
36
+ const now = deps.now ?? Date.now;
37
+ const http = new NetworkHttp(deps.fetch ?? fetch, deps.timeoutMs ?? 30_000);
38
+ const sessions = new NetworkSessions(origin, store, http, now);
39
+ const state = await sessions.read();
40
+ if (command === "logout")
41
+ return await sessions.logout(state);
42
+ if (command === "setup") {
43
+ const response = await http.request(origin, "/SKILL.md");
44
+ if (response.status !== 200)
45
+ throw new NetworkError("SKILL_DOWNLOAD_FAILED");
46
+ const metadata = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/u.exec(response.text)?.[1];
47
+ if (!metadata || !/^name:\s*frely-network\s*$/mu.test(metadata) || !/^description:\s*\S/mu.test(metadata))
48
+ throw new NetworkError("SKILL_INVALID");
49
+ const installed = await files.installSkill(host, response.text);
50
+ await files.writeConfig({ version: 1, origin, host: host });
51
+ const resolved = await sessions.resolve(state);
52
+ if (resolved.kind === "ready")
53
+ return { ...resolved.view, origin, host, cliVersion: VERSION, ...installed };
54
+ if (resolved.kind === "pending")
55
+ return { ...publicPendingGrant(resolved.value), origin, host, cliVersion: VERSION, ...installed };
56
+ const start = await http.request(origin, "/api/network/device/start", { body: { clientName: "Frely CLI", host } });
57
+ const value = http.parse(start);
58
+ if (start.status !== 201)
59
+ throw networkServerError(value);
60
+ const pending = parsePendingGrant(value, origin);
61
+ if (Date.parse(pending.expiresAt) <= now() || Date.parse(pending.expiresAt) > now() + 3_600_000)
62
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
63
+ await sessions.save(pending);
64
+ return { ...publicPendingGrant(pending), origin, host, cliVersion: VERSION, ...installed };
65
+ }
66
+ const resolved = await sessions.resolve(state);
67
+ if (command === "status") {
68
+ if (resolved.kind === "ready")
69
+ return { ...resolved.view, origin };
70
+ if (resolved.kind === "pending")
71
+ return { ...publicPendingGrant(resolved.value), origin };
72
+ return { status: "setup_required", origin, ...(resolved.reason ? { code: resolved.reason } : {}), recovery: "Run frely network setup --json." };
73
+ }
74
+ if (resolved.kind !== "ready")
75
+ throw new NetworkError("NETWORK_SETUP_REQUIRED", {
76
+ recovery: "Run frely network setup --json and approve the browser request.",
77
+ ...(resolved.kind === "pending" ? { verificationUri: resolved.value.verificationUri, userCode: resolved.value.userCode } : {}),
78
+ });
79
+ const token = resolved.credential.accessToken;
80
+ const response = await http.request(origin, `/api/network/capabilities/${command}`, {
81
+ token, ...(id ? { requestId: id } : {}),
82
+ body: command === "find" ? { capabilities: [cap] } : { requestId: id, capabilities: [cap], task, input },
83
+ });
84
+ const value = http.parse(response, [token]);
85
+ if (response.status !== 200) {
86
+ if (response.status === 401)
87
+ await sessions.remove();
88
+ throw networkServerError(value);
89
+ }
90
+ if (value.paymentMode !== "platform_demo")
91
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
92
+ if (command === "find") {
93
+ if (!Array.isArray(value.capabilities))
94
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
95
+ return { capabilities: value.capabilities, paymentMode: "platform_demo" };
96
+ }
97
+ if (value.requestId !== id || value.status !== "succeeded" || !isRecord(value.result) || !isRecord(value.evidence) ||
98
+ value.result.scamProbability !== null || !Number.isSafeInteger(value.remainingCalls) || Number(value.remainingCalls) < 0)
99
+ throw new NetworkError("NETWORK_RESPONSE_INVALID");
100
+ return { requestId: id, status: "succeeded", paymentMode: "platform_demo", result: value.result, evidence: value.evidence, remainingCalls: value.remainingCalls };
101
+ }
102
+ catch (error) {
103
+ const known = error instanceof NetworkError ? error : new NetworkError("NETWORK_CLIENT_FAILED");
104
+ throw id ? new NetworkError(known.code, { ...known.details, requestId: id }) : known;
105
+ }
106
+ finally {
107
+ if (unlock)
108
+ await unlock();
109
+ }
110
+ }
111
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}