percher 0.5.22 → 0.5.23
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/bin/percher.js +564 -439
- package/dist/package.json +3 -3
- package/dist/src/commands/analytics.d.ts +4 -0
- package/dist/src/commands/analytics.d.ts.map +1 -0
- package/dist/src/commands/analytics.js +66 -0
- package/dist/src/commands/analytics.js.map +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/templates/capsule/src/lib/ai-client.ts +193 -0
- package/dist/templates/capsule/src/lib/ai.ts +13 -89
- package/package.json +3 -3
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "percher",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.23",
|
|
4
4
|
"description": "CLI for the Percher hosting platform — publish apps with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@percher/client": "^0.2.
|
|
31
|
-
"@percher/core": "^0.4.
|
|
30
|
+
"@percher/client": "^0.2.22",
|
|
31
|
+
"@percher/core": "^0.4.28",
|
|
32
32
|
"@percher/toml": "^0.1.10",
|
|
33
33
|
"cac": "^7.0.0",
|
|
34
34
|
"picocolors": "^1.1.1"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.d.ts","sourceRoot":"","sources":["../../../src/commands/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAI/B,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CA+DtE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as core from "@percher/core";
|
|
2
|
+
import pc from "picocolors";
|
|
3
|
+
import { writeLine } from "../cli-utils";
|
|
4
|
+
export function registerAnalyticsCommands(cli, ctx) {
|
|
5
|
+
cli
|
|
6
|
+
.command("analytics [app]", "Show visitor analytics for an app")
|
|
7
|
+
.option("--days <n>", "Window in days (1-90, default 7)")
|
|
8
|
+
.action(async (app, options) => {
|
|
9
|
+
// `Number()` is the only conversion here — the RANGE is the core command's
|
|
10
|
+
// to enforce, so there is exactly one place that decides what a valid
|
|
11
|
+
// window is. `Number("abc")` is NaN, which the schema rejects with the
|
|
12
|
+
// same message as 0 or 365.
|
|
13
|
+
const days = options.days === undefined ? undefined : Number(options.days);
|
|
14
|
+
const a = await core.analytics(ctx, { app, days });
|
|
15
|
+
await writeLine(ctx, pc.bold(`Visitors — last ${a.period}`));
|
|
16
|
+
await writeLine(ctx, "");
|
|
17
|
+
// Daily uniques SUMMED: identity is per-UTC-day, so nothing can dedupe
|
|
18
|
+
// across days. The label says so rather than implying otherwise.
|
|
19
|
+
await writeLine(ctx, ` Unique visitors ${a.totalVisitors.toLocaleString()} ${pc.dim("(daily, summed)")}`);
|
|
20
|
+
await writeLine(ctx, ` Page views ${a.totalPageviews.toLocaleString()}`);
|
|
21
|
+
await writeLine(ctx, ` Requests ${a.totalRequests.toLocaleString()}`);
|
|
22
|
+
await writeLine(ctx, ` Sessions ${a.totalSessions.toLocaleString()}`);
|
|
23
|
+
await writeLine(ctx, ` Bounce rate ${a.totalSessions > 0 ? `${a.bounceRate}%` : "—"}`);
|
|
24
|
+
await writeLine(ctx, ` Error rate ${a.errorRate}%`);
|
|
25
|
+
await writeLine(ctx, pc.dim(` Filtered ${(a.totalBotRequests + a.totalScannerRequests).toLocaleString()} bot/scanner requests`));
|
|
26
|
+
await printList(ctx, "Top pages", a.topPages.map((p) => [p.path, p.views]));
|
|
27
|
+
await printList(ctx, "Traffic sources", a.topReferrers.map((r) => [r.source, r.visits]));
|
|
28
|
+
await printList(ctx, "Entry pages", a.topEntryPaths.map((p) => [p.path, p.sessions]));
|
|
29
|
+
await printList(ctx, "Countries", a.byCountry.map((c) => [c.country, c.visitors]));
|
|
30
|
+
await printList(ctx, "Devices", a.byDevice.map((d) => [d.key, d.visitors]));
|
|
31
|
+
if (a.totalRequests === 0 && a.totalBotRequests === 0) {
|
|
32
|
+
await writeLine(ctx, "");
|
|
33
|
+
await writeLine(ctx, pc.dim("No traffic recorded yet. Analytics update every 15 minutes."));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Strip anything that lets a stored label control the terminal.
|
|
39
|
+
*
|
|
40
|
+
* Paths, referrer hosts and device strings all originate from request traffic,
|
|
41
|
+
* so a visitor chooses them. Printed raw, an OSC-8 sequence renders as a
|
|
42
|
+
* clickable link to somewhere else entirely, and a bidi override reorders the
|
|
43
|
+
* text so `/safe\u202Etxt.exe` reads as something it is not. C0/C1, CSI/OSC and
|
|
44
|
+
* the bidi controls all go.
|
|
45
|
+
*/
|
|
46
|
+
function safeLabel(label) {
|
|
47
|
+
return (label
|
|
48
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point
|
|
49
|
+
.replace(/\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g, "")
|
|
50
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point
|
|
51
|
+
.replace(/\u001b\[[0-9;?]*[ -/]*[@-~]/g, "")
|
|
52
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: stripping them is the point
|
|
53
|
+
.replace(/[\u0000-\u001f\u007f-\u009f]/g, "")
|
|
54
|
+
.replace(/[\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "")
|
|
55
|
+
.slice(0, 120));
|
|
56
|
+
}
|
|
57
|
+
async function printList(ctx, title, rows) {
|
|
58
|
+
if (rows.length === 0)
|
|
59
|
+
return;
|
|
60
|
+
await writeLine(ctx, "");
|
|
61
|
+
await writeLine(ctx, pc.bold(title));
|
|
62
|
+
for (const [label, value] of rows.slice(0, 10)) {
|
|
63
|
+
await writeLine(ctx, ` ${String(value).padStart(7)} ${safeLabel(label)}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=analytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../../src/commands/analytics.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAwB7C,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAuEhF"}
|
package/dist/src/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import cac from "cac";
|
|
|
2
2
|
import { SilentExitError, writeUnknownCommand } from "./cli-utils";
|
|
3
3
|
import { registerAccountCommands } from "./commands/account";
|
|
4
4
|
import { registerAdminCommands } from "./commands/admin";
|
|
5
|
+
import { registerAnalyticsCommands } from "./commands/analytics";
|
|
5
6
|
import { registerAppsCommands } from "./commands/apps";
|
|
6
7
|
import { registerAuthCommands } from "./commands/auth";
|
|
7
8
|
import { registerDataCommands } from "./commands/data";
|
|
@@ -23,6 +24,7 @@ export async function run(argv, ctxOverride) {
|
|
|
23
24
|
const cli = cac("percher");
|
|
24
25
|
const ctx = ctxOverride ?? (await createCliContext());
|
|
25
26
|
registerAppsCommands(cli, ctx);
|
|
27
|
+
registerAnalyticsCommands(cli, ctx);
|
|
26
28
|
registerDeploysCommands(cli, ctx, argv);
|
|
27
29
|
registerEnvCommands(cli, ctx);
|
|
28
30
|
registerExportCommands(cli, ctx);
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
export type ManagedInferenceOptions = {
|
|
2
|
+
model?: string;
|
|
3
|
+
maxTokens?: number;
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export class ManagedInferenceError extends Error {
|
|
8
|
+
constructor(
|
|
9
|
+
message: string,
|
|
10
|
+
readonly status?: number,
|
|
11
|
+
readonly code?: string,
|
|
12
|
+
readonly retryAt?: string,
|
|
13
|
+
) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = "ManagedInferenceError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createManagedInferenceClient(config: {
|
|
20
|
+
endpoint?: string;
|
|
21
|
+
getSession: () => { valid: boolean; token: string };
|
|
22
|
+
fetch?: typeof globalThis.fetch;
|
|
23
|
+
}) {
|
|
24
|
+
const fetcher = config.fetch ?? globalThis.fetch;
|
|
25
|
+
|
|
26
|
+
function isConfigured(): boolean {
|
|
27
|
+
return Boolean(config.endpoint);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function assertReady(): string {
|
|
31
|
+
if (!config.endpoint) {
|
|
32
|
+
throw new ManagedInferenceError(
|
|
33
|
+
"Managed inference is not available for this deployment.",
|
|
34
|
+
503,
|
|
35
|
+
"INFERENCE_DISABLED",
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const session = config.getSession();
|
|
39
|
+
if (!session.valid || !session.token) {
|
|
40
|
+
throw new ManagedInferenceError("Sign in to use AI.", 401, "INFERENCE_AUTH_REQUIRED");
|
|
41
|
+
}
|
|
42
|
+
return session.token;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function safeError(response: Response): Promise<ManagedInferenceError> {
|
|
46
|
+
let code: string | undefined;
|
|
47
|
+
let resetAt: string | undefined;
|
|
48
|
+
try {
|
|
49
|
+
const body = (await response.json()) as {
|
|
50
|
+
error?: { code?: unknown; resetAt?: unknown; retryAfterSec?: unknown };
|
|
51
|
+
};
|
|
52
|
+
if (typeof body.error?.code === "string") code = body.error.code;
|
|
53
|
+
if (typeof body.error?.resetAt === "string") resetAt = body.error.resetAt;
|
|
54
|
+
if (!resetAt && typeof body.error?.retryAfterSec === "number") {
|
|
55
|
+
resetAt = new Date(Date.now() + body.error.retryAfterSec * 1000).toISOString();
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
// Never show raw upstream/proxy response bodies to capsule users.
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (response.status === 401) {
|
|
62
|
+
return new ManagedInferenceError("Your session expired. Sign in again.", 401, code);
|
|
63
|
+
}
|
|
64
|
+
if (response.status === 403) {
|
|
65
|
+
return new ManagedInferenceError(
|
|
66
|
+
"Managed inference is not included for this app's plan.",
|
|
67
|
+
403,
|
|
68
|
+
code,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (response.status === 429) {
|
|
72
|
+
return new ManagedInferenceError(
|
|
73
|
+
resetAt ? `AI usage is limited until ${resetAt}.` : "AI usage is temporarily limited.",
|
|
74
|
+
429,
|
|
75
|
+
code,
|
|
76
|
+
resetAt,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (response.status === 503) {
|
|
80
|
+
return new ManagedInferenceError(
|
|
81
|
+
"Managed inference is currently unavailable.",
|
|
82
|
+
503,
|
|
83
|
+
code ?? "INFERENCE_DISABLED",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return new ManagedInferenceError(
|
|
87
|
+
"The AI request could not be completed. Try again.",
|
|
88
|
+
response.status,
|
|
89
|
+
code,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function request(prompt: string, opts: ManagedInferenceOptions, stream: boolean) {
|
|
94
|
+
// Validate configuration/auth before the network-error wrapper so a local
|
|
95
|
+
// disabled or signed-out state keeps its precise user-facing error.
|
|
96
|
+
const authorization = `Bearer ${assertReady()}`;
|
|
97
|
+
let response: Response;
|
|
98
|
+
try {
|
|
99
|
+
response = await fetcher(config.endpoint!, {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: {
|
|
102
|
+
"content-type": "application/json",
|
|
103
|
+
authorization,
|
|
104
|
+
},
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
model: opts.model,
|
|
107
|
+
max_tokens: opts.maxTokens ?? 512,
|
|
108
|
+
messages: [{ role: "user", content: prompt }],
|
|
109
|
+
...(stream ? { stream: true } : {}),
|
|
110
|
+
}),
|
|
111
|
+
signal: opts.signal,
|
|
112
|
+
});
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (opts.signal?.aborted) throw error;
|
|
115
|
+
throw new ManagedInferenceError("Managed inference could not be reached. Try again.");
|
|
116
|
+
}
|
|
117
|
+
if (!response.ok) throw await safeError(response);
|
|
118
|
+
return response;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function complete(
|
|
122
|
+
prompt: string,
|
|
123
|
+
opts: ManagedInferenceOptions = {},
|
|
124
|
+
): Promise<string> {
|
|
125
|
+
const response = await request(prompt, opts, false);
|
|
126
|
+
const data = (await response.json()) as {
|
|
127
|
+
choices?: Array<{ message?: { content?: unknown } }>;
|
|
128
|
+
};
|
|
129
|
+
const content = data.choices?.[0]?.message?.content;
|
|
130
|
+
if (typeof content !== "string") {
|
|
131
|
+
throw new ManagedInferenceError("The AI provider returned an invalid response.");
|
|
132
|
+
}
|
|
133
|
+
return content;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function streamComplete(
|
|
137
|
+
prompt: string,
|
|
138
|
+
onToken: (delta: string) => void,
|
|
139
|
+
opts: ManagedInferenceOptions = {},
|
|
140
|
+
): Promise<string> {
|
|
141
|
+
const response = await request(prompt, opts, true);
|
|
142
|
+
if (!response.body) throw new ManagedInferenceError("The AI stream could not be opened.");
|
|
143
|
+
|
|
144
|
+
const reader = response.body.getReader();
|
|
145
|
+
const decoder = new TextDecoder();
|
|
146
|
+
let buffer = "";
|
|
147
|
+
let full = "";
|
|
148
|
+
let sawDone = false;
|
|
149
|
+
try {
|
|
150
|
+
for (;;) {
|
|
151
|
+
const { done, value } = await reader.read();
|
|
152
|
+
if (done) break;
|
|
153
|
+
buffer += decoder.decode(value, { stream: true }).replaceAll("\r\n", "\n");
|
|
154
|
+
let separator = buffer.indexOf("\n\n");
|
|
155
|
+
while (separator !== -1) {
|
|
156
|
+
const event = buffer.slice(0, separator);
|
|
157
|
+
buffer = buffer.slice(separator + 2);
|
|
158
|
+
separator = buffer.indexOf("\n\n");
|
|
159
|
+
const payload = event
|
|
160
|
+
.split("\n")
|
|
161
|
+
.filter((line) => line.startsWith("data:"))
|
|
162
|
+
.map((line) => line.slice(5).trimStart())
|
|
163
|
+
.join("\n")
|
|
164
|
+
.trim();
|
|
165
|
+
if (!payload) continue;
|
|
166
|
+
if (payload === "[DONE]") {
|
|
167
|
+
sawDone = true;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const chunk = JSON.parse(payload) as {
|
|
172
|
+
choices?: Array<{ delta?: { content?: unknown } }>;
|
|
173
|
+
};
|
|
174
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
175
|
+
if (typeof delta === "string" && delta) {
|
|
176
|
+
full += delta;
|
|
177
|
+
onToken(delta);
|
|
178
|
+
}
|
|
179
|
+
} catch {
|
|
180
|
+
// Ignore SSE comments/keep-alives and malformed non-data events.
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!sawDone) throw new ManagedInferenceError("The AI stream ended unexpectedly.");
|
|
185
|
+
return full;
|
|
186
|
+
} finally {
|
|
187
|
+
if (opts.signal?.aborted) await reader.cancel().catch(() => undefined);
|
|
188
|
+
reader.releaseLock();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return { isConfigured, complete, streamComplete };
|
|
193
|
+
}
|
|
@@ -1,91 +1,15 @@
|
|
|
1
|
+
import { createManagedInferenceClient } from "./ai-client";
|
|
1
2
|
import { pb } from "./db";
|
|
2
3
|
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
prompt: string,
|
|
16
|
-
opts?: { model?: string; maxTokens?: number },
|
|
17
|
-
): Promise<string> {
|
|
18
|
-
if (!AI_URL) throw new Error("Managed inference isn't configured for this app.");
|
|
19
|
-
if (!pb.authStore.isValid) throw new Error("Sign in to use AI.");
|
|
20
|
-
const res = await fetch(AI_URL, {
|
|
21
|
-
method: "POST",
|
|
22
|
-
headers: { "content-type": "application/json", authorization: pb.authStore.token },
|
|
23
|
-
body: JSON.stringify({
|
|
24
|
-
model: opts?.model,
|
|
25
|
-
max_tokens: opts?.maxTokens ?? 512,
|
|
26
|
-
messages: [{ role: "user", content: prompt }],
|
|
27
|
-
}),
|
|
28
|
-
});
|
|
29
|
-
if (!res.ok) throw new Error(`AI request failed (${res.status})`);
|
|
30
|
-
const data = (await res.json()) as { choices?: Array<{ message?: { content?: string } }> };
|
|
31
|
-
return data.choices?.[0]?.message?.content ?? "";
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Streaming variant of {@link complete} — calls `onToken` with each text delta
|
|
36
|
-
* as it arrives and resolves with the full text. Same auth + billing as
|
|
37
|
-
* `complete` (keyless, billed to the app owner). Use it for a typewriter UI:
|
|
38
|
-
*
|
|
39
|
-
* const full = await streamComplete("Write a haiku", (t) => { output.value += t; });
|
|
40
|
-
*/
|
|
41
|
-
export async function streamComplete(
|
|
42
|
-
prompt: string,
|
|
43
|
-
onToken: (delta: string) => void,
|
|
44
|
-
opts?: { model?: string; maxTokens?: number },
|
|
45
|
-
): Promise<string> {
|
|
46
|
-
if (!AI_URL) throw new Error("Managed inference isn't configured for this app.");
|
|
47
|
-
if (!pb.authStore.isValid) throw new Error("Sign in to use AI.");
|
|
48
|
-
const res = await fetch(AI_URL, {
|
|
49
|
-
method: "POST",
|
|
50
|
-
headers: { "content-type": "application/json", authorization: pb.authStore.token },
|
|
51
|
-
body: JSON.stringify({
|
|
52
|
-
model: opts?.model,
|
|
53
|
-
max_tokens: opts?.maxTokens ?? 512,
|
|
54
|
-
messages: [{ role: "user", content: prompt }],
|
|
55
|
-
stream: true,
|
|
56
|
-
}),
|
|
57
|
-
});
|
|
58
|
-
if (!res.ok || !res.body) throw new Error(`AI request failed (${res.status})`);
|
|
59
|
-
|
|
60
|
-
const reader = res.body.getReader();
|
|
61
|
-
const decoder = new TextDecoder();
|
|
62
|
-
let buffer = "";
|
|
63
|
-
let full = "";
|
|
64
|
-
for (;;) {
|
|
65
|
-
const { done, value } = await reader.read();
|
|
66
|
-
if (done) break;
|
|
67
|
-
buffer += decoder.decode(value, { stream: true });
|
|
68
|
-
// SSE events are separated by a blank line; parse each complete one.
|
|
69
|
-
let sep = buffer.indexOf("\n\n");
|
|
70
|
-
while (sep !== -1) {
|
|
71
|
-
const event = buffer.slice(0, sep);
|
|
72
|
-
buffer = buffer.slice(sep + 2);
|
|
73
|
-
sep = buffer.indexOf("\n\n");
|
|
74
|
-
const line = event.startsWith("data:") ? event.slice(5).trim() : "";
|
|
75
|
-
if (!line || line === "[DONE]") continue;
|
|
76
|
-
try {
|
|
77
|
-
const chunk = JSON.parse(line) as {
|
|
78
|
-
choices?: Array<{ delta?: { content?: string } }>;
|
|
79
|
-
};
|
|
80
|
-
const delta = chunk.choices?.[0]?.delta?.content ?? "";
|
|
81
|
-
if (delta) {
|
|
82
|
-
full += delta;
|
|
83
|
-
onToken(delta);
|
|
84
|
-
}
|
|
85
|
-
} catch {
|
|
86
|
-
/* ignore keep-alive / non-JSON lines */
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return full;
|
|
91
|
-
}
|
|
4
|
+
// This is a public URL, never a provider credential. Percher injects it only
|
|
5
|
+
// when the shared server-side feature gate is operational.
|
|
6
|
+
const client = createManagedInferenceClient({
|
|
7
|
+
endpoint: import.meta.env.VITE_PERCHER_AI_URL as string | undefined,
|
|
8
|
+
getSession: () => ({ valid: pb.authStore.isValid, token: pb.authStore.token }),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export { ManagedInferenceError } from "./ai-client";
|
|
12
|
+
export type { ManagedInferenceOptions } from "./ai-client";
|
|
13
|
+
export const isManagedInferenceConfigured = client.isConfigured;
|
|
14
|
+
export const complete = client.complete;
|
|
15
|
+
export const streamComplete = client.streamComplete;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "percher",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.23",
|
|
4
4
|
"description": "CLI for the Percher hosting platform — publish apps with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"typecheck": "tsc --noEmit"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@percher/client": "^0.2.
|
|
31
|
-
"@percher/core": "^0.4.
|
|
30
|
+
"@percher/client": "^0.2.22",
|
|
31
|
+
"@percher/core": "^0.4.28",
|
|
32
32
|
"@percher/toml": "^0.1.10",
|
|
33
33
|
"cac": "^7.0.0",
|
|
34
34
|
"picocolors": "^1.1.1"
|