@madewithremy/admin 0.1.2 → 0.1.4
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/index.d.ts +245 -1
- package/dist/index.js +55 -10
- package/dist/index.js.map +1 -1
- package/dist/prod.js +300 -4
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -1887,6 +1887,82 @@ async function streamToStdout(ctx, apiPath, body) {
|
|
|
1887
1887
|
}
|
|
1888
1888
|
}
|
|
1889
1889
|
}
|
|
1890
|
+
async function tailSseToStdout(ctx, apiPath) {
|
|
1891
|
+
const controller = new AbortController();
|
|
1892
|
+
let idleTimer;
|
|
1893
|
+
let idled = false;
|
|
1894
|
+
const armIdleTimer = () => {
|
|
1895
|
+
if (idleTimer) {
|
|
1896
|
+
clearTimeout(idleTimer);
|
|
1897
|
+
}
|
|
1898
|
+
idleTimer = setTimeout(() => {
|
|
1899
|
+
idled = true;
|
|
1900
|
+
controller.abort();
|
|
1901
|
+
}, STREAM_IDLE_TIMEOUT_MS);
|
|
1902
|
+
};
|
|
1903
|
+
let complete = false;
|
|
1904
|
+
try {
|
|
1905
|
+
armIdleTimer();
|
|
1906
|
+
const res = await fetch(`${ctx.baseUrl}${apiPath}`, {
|
|
1907
|
+
method: "GET",
|
|
1908
|
+
headers: { ...authHeaders(ctx), Accept: "text/event-stream" },
|
|
1909
|
+
signal: controller.signal
|
|
1910
|
+
});
|
|
1911
|
+
if (!res.ok) {
|
|
1912
|
+
fatal(
|
|
1913
|
+
`API GET ${apiPath} returned ${res.status}: ${JSON.stringify(await readBody(res))}`
|
|
1914
|
+
);
|
|
1915
|
+
}
|
|
1916
|
+
if (!res.body) {
|
|
1917
|
+
fatal("Tail response has no body");
|
|
1918
|
+
}
|
|
1919
|
+
const reader = res.body.getReader();
|
|
1920
|
+
const decoder = new TextDecoder();
|
|
1921
|
+
let buffer = "";
|
|
1922
|
+
const emit = (line) => {
|
|
1923
|
+
if (!line.startsWith("data: ")) {
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
try {
|
|
1927
|
+
const value = JSON.parse(line.slice(6));
|
|
1928
|
+
if (value?.type === "tail_complete") {
|
|
1929
|
+
complete = true;
|
|
1930
|
+
return;
|
|
1931
|
+
}
|
|
1932
|
+
out(value);
|
|
1933
|
+
} catch {
|
|
1934
|
+
}
|
|
1935
|
+
};
|
|
1936
|
+
while (true) {
|
|
1937
|
+
const { done, value } = await reader.read();
|
|
1938
|
+
if (done) {
|
|
1939
|
+
emit(buffer);
|
|
1940
|
+
break;
|
|
1941
|
+
}
|
|
1942
|
+
armIdleTimer();
|
|
1943
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1944
|
+
const lines = buffer.split("\n");
|
|
1945
|
+
buffer = lines.pop();
|
|
1946
|
+
for (const line of lines) {
|
|
1947
|
+
emit(line);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
} catch (err) {
|
|
1951
|
+
if (complete) {
|
|
1952
|
+
return;
|
|
1953
|
+
}
|
|
1954
|
+
if (idled || err?.name === "AbortError") {
|
|
1955
|
+
fatal(
|
|
1956
|
+
`Tail GET ${apiPath} stalled \u2014 no data for ${STREAM_IDLE_TIMEOUT_MS / 1e3}s`
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
throw err;
|
|
1960
|
+
} finally {
|
|
1961
|
+
if (idleTimer) {
|
|
1962
|
+
clearTimeout(idleTimer);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1890
1966
|
|
|
1891
1967
|
// src/ops/methods.ts
|
|
1892
1968
|
async function list6(ctx) {
|
|
@@ -2381,8 +2457,9 @@ var prerenderSpecs = {
|
|
|
2381
2457
|
}
|
|
2382
2458
|
},
|
|
2383
2459
|
"prerender get": {
|
|
2384
|
-
usage: "Usage: remy-admin prerender get <path>",
|
|
2385
|
-
positionals: [{ name: "path", required: true }]
|
|
2460
|
+
usage: "Usage: remy-admin prerender get <path> [--host <hostname>]",
|
|
2461
|
+
positionals: [{ name: "path", required: true }],
|
|
2462
|
+
flags: { host: { type: "string" } }
|
|
2386
2463
|
},
|
|
2387
2464
|
"prerender pages": {
|
|
2388
2465
|
usage: "Usage: remy-admin prerender pages [--cursor <c>]",
|
|
@@ -2404,7 +2481,7 @@ async function prerenderGet(ctx, a) {
|
|
|
2404
2481
|
const rawPath = a.req("path");
|
|
2405
2482
|
const normalized = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
|
|
2406
2483
|
const isDev = !ctx.baseUrl.includes("api.mindstudio.ai");
|
|
2407
|
-
const host = `${ctx.appId}${isDev ? "-dev" : ""}.madewithremy.com`;
|
|
2484
|
+
const host = a.str("host")?.replace(/^https?:\/\//, "").replace(/\/+$/, "") || `${ctx.appId}${isDev ? "-dev" : ""}.madewithremy.com`;
|
|
2408
2485
|
const url = `https://${host}${normalized}`;
|
|
2409
2486
|
const res = await fetchWithTimeout(
|
|
2410
2487
|
url,
|
|
@@ -2448,6 +2525,7 @@ Usage:
|
|
|
2448
2525
|
remy-admin prerender invalidate <path...> Purge specific paths
|
|
2449
2526
|
remy-admin prerender invalidate --all Purge every snapshot for the app
|
|
2450
2527
|
remy-admin prerender get <path> Print { url, status, html } as a crawler sees it
|
|
2528
|
+
remy-admin prerender get <path> --host <h> Same, against a specific hostname
|
|
2451
2529
|
|
|
2452
2530
|
Notes:
|
|
2453
2531
|
- 'pages'/'view' read what's stored for the LIVE release; 'get' exercises the real
|
|
@@ -2455,12 +2533,17 @@ Notes:
|
|
|
2455
2533
|
returns the live SPA and triggers a render \u2014 re-run to see it.
|
|
2456
2534
|
- 'view' errors with snapshot_not_found when the path has no snapshot yet.
|
|
2457
2535
|
- Invalidation targets the current LIVE release; a deploy already invalidates on its own.
|
|
2536
|
+
- Mounted pages: when another app mounts this one, 'pages' lists paths carrying that
|
|
2537
|
+
app's mount prefix, and those pages exist only on that app's host \u2014 pass --host to
|
|
2538
|
+
verify them with 'get'. 'invalidate' takes this app's OWN paths and purges the
|
|
2539
|
+
mounted copies with them.
|
|
2458
2540
|
|
|
2459
2541
|
Examples:
|
|
2460
2542
|
remy-admin prerender pages
|
|
2461
2543
|
remy-admin prerender view /u/abc123
|
|
2462
2544
|
remy-admin prerender invalidate --all
|
|
2463
|
-
remy-admin prerender get /u/abc123
|
|
2545
|
+
remy-admin prerender get /u/abc123
|
|
2546
|
+
remy-admin prerender get /demos/vector-search/blog/x --host example.com`;
|
|
2464
2547
|
|
|
2465
2548
|
// src/commands/files.ts
|
|
2466
2549
|
import fs2 from "fs";
|
|
@@ -3781,6 +3864,209 @@ Notes:
|
|
|
3781
3864
|
Transcripts ('sessions get') are the primary way to debug and iterate on a
|
|
3782
3865
|
voice persona.`;
|
|
3783
3866
|
|
|
3867
|
+
// src/ops/agent.ts
|
|
3868
|
+
function threadsList(ctx, params = {}) {
|
|
3869
|
+
return call(
|
|
3870
|
+
ctx,
|
|
3871
|
+
"GET",
|
|
3872
|
+
`/_internal/v2/apps/${ctx.appId}/agent-threads${qs(params)}`
|
|
3873
|
+
);
|
|
3874
|
+
}
|
|
3875
|
+
function threadsGet(ctx, threadId) {
|
|
3876
|
+
return call(
|
|
3877
|
+
ctx,
|
|
3878
|
+
"GET",
|
|
3879
|
+
`/_internal/v2/apps/${ctx.appId}/agent-threads/${seg(threadId)}`
|
|
3880
|
+
);
|
|
3881
|
+
}
|
|
3882
|
+
|
|
3883
|
+
// src/commands/agent.ts
|
|
3884
|
+
var agentSpecs = {
|
|
3885
|
+
"agent threads list": {
|
|
3886
|
+
usage: "Usage: remy-admin agent threads list [--limit 20] [--cursor <nextCursor>]",
|
|
3887
|
+
flags: {
|
|
3888
|
+
limit: { type: "number", min: 1, max: 100 },
|
|
3889
|
+
cursor: { type: "string" }
|
|
3890
|
+
}
|
|
3891
|
+
},
|
|
3892
|
+
"agent threads get": {
|
|
3893
|
+
usage: "Usage: remy-admin agent threads get <threadId>",
|
|
3894
|
+
positionals: [{ name: "threadId", required: true }]
|
|
3895
|
+
}
|
|
3896
|
+
};
|
|
3897
|
+
async function threadsList2(ctx, a) {
|
|
3898
|
+
out(
|
|
3899
|
+
await threadsList(ctx, {
|
|
3900
|
+
limit: a.num("limit"),
|
|
3901
|
+
cursor: a.str("cursor")
|
|
3902
|
+
})
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
async function threadsGet2(ctx, a) {
|
|
3906
|
+
out(await threadsGet(ctx, a.req("threadId")));
|
|
3907
|
+
}
|
|
3908
|
+
var agentHandlers = {
|
|
3909
|
+
"agent threads list": threadsList2,
|
|
3910
|
+
"agent threads get": threadsGet2
|
|
3911
|
+
};
|
|
3912
|
+
var agentHelp = `remy-admin agent \u2014 Conversation log for the app's agent (chat) interface.
|
|
3913
|
+
|
|
3914
|
+
Subcommands:
|
|
3915
|
+
threads list Every conversation, newest activity first (transcripts excluded)
|
|
3916
|
+
threads get One conversation with its full transcript
|
|
3917
|
+
|
|
3918
|
+
Usage:
|
|
3919
|
+
remy-admin agent threads list [--limit 20] [--cursor <nextCursor>]
|
|
3920
|
+
remy-admin agent threads get <threadId>
|
|
3921
|
+
|
|
3922
|
+
Examples:
|
|
3923
|
+
remy-admin agent threads list --limit 10
|
|
3924
|
+
remy-admin agent threads get 4f6c\u2026
|
|
3925
|
+
|
|
3926
|
+
Notes:
|
|
3927
|
+
Transcripts are how you iterate on an agent: after the user tests it, read
|
|
3928
|
+
'threads get' for what was actually said and which tools ran with which
|
|
3929
|
+
arguments, then fix the system prompt and tool descriptions from that evidence
|
|
3930
|
+
rather than from guesses.
|
|
3931
|
+
|
|
3932
|
+
In the list, 'toolErrorCount' and 'hasTurnError' point at the conversations
|
|
3933
|
+
worth opening \u2014 a failed tool call, or a turn that broke (model error, rate
|
|
3934
|
+
limit, credits) instead of replying. 'devSession' is true for your own test
|
|
3935
|
+
conversations through the dev tunnel, so you can tell them from real traffic.
|
|
3936
|
+
|
|
3937
|
+
In a transcript, each message is the stored conversation's own shape: 'user'
|
|
3938
|
+
for a person's message and also for a tool result (which carries
|
|
3939
|
+
'toolCallId'), 'assistant' for the agent (carrying 'toolCalls' when it asked
|
|
3940
|
+
for tools). A method tool call also carries a 'requestId' \u2014 pass it to
|
|
3941
|
+
'remy-admin requests get <requestId>' for that call's input, output, stdout
|
|
3942
|
+
and error. Client tools run in the browser, so they have no requestId.`;
|
|
3943
|
+
|
|
3944
|
+
// src/ops/events.ts
|
|
3945
|
+
function channelsList(ctx, params = {}) {
|
|
3946
|
+
return call(
|
|
3947
|
+
ctx,
|
|
3948
|
+
"GET",
|
|
3949
|
+
`/_internal/v2/apps/${ctx.appId}/events/channels${qs(params)}`
|
|
3950
|
+
);
|
|
3951
|
+
}
|
|
3952
|
+
function publish(ctx, params) {
|
|
3953
|
+
return call(
|
|
3954
|
+
ctx,
|
|
3955
|
+
"POST",
|
|
3956
|
+
`/_internal/v2/apps/${ctx.appId}/events/publish`,
|
|
3957
|
+
{ channels: params.channels, data: params.data, scope: params.scope }
|
|
3958
|
+
);
|
|
3959
|
+
}
|
|
3960
|
+
|
|
3961
|
+
// src/commands/events.ts
|
|
3962
|
+
var eventsSpecs = {
|
|
3963
|
+
"events tail": {
|
|
3964
|
+
usage: "Usage: remy-admin events tail [<channel>] [--scope live] [--for 60]",
|
|
3965
|
+
positionals: [{ name: "channel" }],
|
|
3966
|
+
flags: {
|
|
3967
|
+
scope: { type: "string" },
|
|
3968
|
+
for: { type: "number", min: 1, max: 600 }
|
|
3969
|
+
}
|
|
3970
|
+
},
|
|
3971
|
+
"events publish": {
|
|
3972
|
+
usage: "Usage: remy-admin events publish <channel> <json> [--scope live]\nPublishes exactly as the app's own events.publish would \u2014 same validation, delivery, and metering.",
|
|
3973
|
+
positionals: [
|
|
3974
|
+
{ name: "channel", required: true },
|
|
3975
|
+
{ name: "json", required: true }
|
|
3976
|
+
],
|
|
3977
|
+
flags: {
|
|
3978
|
+
scope: { type: "string" }
|
|
3979
|
+
}
|
|
3980
|
+
},
|
|
3981
|
+
"events channels list": {
|
|
3982
|
+
usage: "Usage: remy-admin events channels list [--scope live]",
|
|
3983
|
+
flags: {
|
|
3984
|
+
scope: { type: "string" }
|
|
3985
|
+
}
|
|
3986
|
+
}
|
|
3987
|
+
};
|
|
3988
|
+
async function eventsTail(ctx, a) {
|
|
3989
|
+
const params = new URLSearchParams();
|
|
3990
|
+
const channel = a.str("channel");
|
|
3991
|
+
if (channel) {
|
|
3992
|
+
params.set("channel", channel);
|
|
3993
|
+
}
|
|
3994
|
+
const scope = a.str("scope");
|
|
3995
|
+
if (scope) {
|
|
3996
|
+
params.set("scope", scope);
|
|
3997
|
+
}
|
|
3998
|
+
const forSeconds = a.num("for");
|
|
3999
|
+
if (forSeconds !== void 0) {
|
|
4000
|
+
params.set("forSeconds", String(forSeconds));
|
|
4001
|
+
}
|
|
4002
|
+
const query3 = params.toString();
|
|
4003
|
+
await tailSseToStdout(
|
|
4004
|
+
ctx,
|
|
4005
|
+
`/_internal/v2/apps/${ctx.appId}/events/tail${query3 ? `?${query3}` : ""}`
|
|
4006
|
+
);
|
|
4007
|
+
}
|
|
4008
|
+
async function eventsPublish(ctx, a) {
|
|
4009
|
+
const raw = a.req("json");
|
|
4010
|
+
let data;
|
|
4011
|
+
try {
|
|
4012
|
+
data = JSON.parse(raw);
|
|
4013
|
+
} catch {
|
|
4014
|
+
fatal(`Invalid JSON payload: ${raw}`);
|
|
4015
|
+
}
|
|
4016
|
+
out(
|
|
4017
|
+
await publish(ctx, {
|
|
4018
|
+
channels: a.req("channel"),
|
|
4019
|
+
data,
|
|
4020
|
+
scope: a.str("scope")
|
|
4021
|
+
})
|
|
4022
|
+
);
|
|
4023
|
+
}
|
|
4024
|
+
async function eventsChannelsList(ctx, a) {
|
|
4025
|
+
out(await channelsList(ctx, { scope: a.str("scope") }));
|
|
4026
|
+
}
|
|
4027
|
+
var eventsHandlers = {
|
|
4028
|
+
"events tail": eventsTail,
|
|
4029
|
+
"events publish": eventsPublish,
|
|
4030
|
+
"events channels list": eventsChannelsList
|
|
4031
|
+
};
|
|
4032
|
+
var eventsHelp = `remy-admin events \u2014 Observe and test the app's realtime events (publish/subscribe).
|
|
4033
|
+
|
|
4034
|
+
Subcommands:
|
|
4035
|
+
tail Print publishes live as they happen (bounded; default 60s, max 600)
|
|
4036
|
+
publish Publish a test event, exactly as the app's own events.publish would
|
|
4037
|
+
channels list Channels with recent publishes/subscriptions + live subscriber counts
|
|
4038
|
+
|
|
4039
|
+
Usage:
|
|
4040
|
+
remy-admin events tail [<channel>] [--scope live] [--for 60]
|
|
4041
|
+
remy-admin events publish <channel> <json> [--scope live]
|
|
4042
|
+
remy-admin events channels list [--scope live]
|
|
4043
|
+
|
|
4044
|
+
Examples:
|
|
4045
|
+
remy-admin events tail --for 30
|
|
4046
|
+
remy-admin events tail jobs:usr_123
|
|
4047
|
+
remy-admin events publish jobs:usr_123 '{"type":"ping"}'
|
|
4048
|
+
remy-admin events channels list
|
|
4049
|
+
|
|
4050
|
+
Notes:
|
|
4051
|
+
tail answers "is my backend publishing what I think it is": run it in one
|
|
4052
|
+
shell, trigger the publishing method in another, and each matching publish
|
|
4053
|
+
prints as one JSON value ({channel, data, ts}). It ends on its own after
|
|
4054
|
+
--for seconds (exit 0), so it is safe to run from a script.
|
|
4055
|
+
|
|
4056
|
+
publish lets a frontend subscriber be verified before the backend trigger
|
|
4057
|
+
exists. The response's 'delivered' counts live subscriber connections per
|
|
4058
|
+
published channel \u2014 0 means nobody is listening right now, which is normal,
|
|
4059
|
+
not an error.
|
|
4060
|
+
|
|
4061
|
+
channels list is the mismatch-finder: a channel with subscribers but no
|
|
4062
|
+
recent publish (or the reverse) usually means the frontend and backend spell
|
|
4063
|
+
the channel differently. Window is 24h; channels are exact strings \u2014 there
|
|
4064
|
+
are no wildcards anywhere in app events.
|
|
4065
|
+
|
|
4066
|
+
Scopes: events never cross execution environments. --scope defaults to
|
|
4067
|
+
'live'; use 'dev:<devSessionId>' to inspect a tunnel session's world or
|
|
4068
|
+
'preview:<releaseId>' for a branch preview.`;
|
|
4069
|
+
|
|
3784
4070
|
// src/ops/jewels.ts
|
|
3785
4071
|
var JEWEL_RUN_TIMEOUT_MS = 6e5;
|
|
3786
4072
|
function overview(ctx, params = {}) {
|
|
@@ -5063,6 +5349,8 @@ var SPECS = {
|
|
|
5063
5349
|
...filesSpecs,
|
|
5064
5350
|
...dataSourcesSpecs,
|
|
5065
5351
|
...voiceSpecs,
|
|
5352
|
+
...agentSpecs,
|
|
5353
|
+
...eventsSpecs,
|
|
5066
5354
|
...jewelsSpecs,
|
|
5067
5355
|
...settingsSpecs,
|
|
5068
5356
|
...emailSpecs,
|
|
@@ -5085,6 +5373,8 @@ var HANDLERS = {
|
|
|
5085
5373
|
...filesHandlers,
|
|
5086
5374
|
...dataSourcesHandlers,
|
|
5087
5375
|
...voiceHandlers,
|
|
5376
|
+
...agentHandlers,
|
|
5377
|
+
...eventsHandlers,
|
|
5088
5378
|
...jewelsHandlers,
|
|
5089
5379
|
...settingsHandlers,
|
|
5090
5380
|
...emailHandlers,
|
|
@@ -5107,6 +5397,8 @@ var GROUP_HELP = {
|
|
|
5107
5397
|
files: filesHelp,
|
|
5108
5398
|
datasources: dataSourcesHelp,
|
|
5109
5399
|
voice: voiceHelp,
|
|
5400
|
+
agent: agentHelp,
|
|
5401
|
+
events: eventsHelp,
|
|
5110
5402
|
jewels: jewelsHelp,
|
|
5111
5403
|
settings: settingsHelp,
|
|
5112
5404
|
email: emailHelp,
|
|
@@ -5186,6 +5478,8 @@ Commands:
|
|
|
5186
5478
|
files Store, retrieve, share (signed links) + manage files in the app's stores
|
|
5187
5479
|
datasources Build and query searchable document corpora (RAG)
|
|
5188
5480
|
voice Phone numbers, voice call log + transcripts, voice policy settings
|
|
5481
|
+
agent Agent (chat) conversation log + transcripts
|
|
5482
|
+
events Realtime events \u2014 live tail, test publish, channel activity
|
|
5189
5483
|
jewels Monitor jewel shadowing; review + approve the proposal queue
|
|
5190
5484
|
settings App settings \u2014 signup allowlist, test accounts, embed origins, toggles
|
|
5191
5485
|
email Email \u2014 delivery log, blast stats, unsubscribes, inbox, custom domains
|
|
@@ -5216,6 +5510,8 @@ var GROUPS = [
|
|
|
5216
5510
|
"files",
|
|
5217
5511
|
"datasources",
|
|
5218
5512
|
"voice",
|
|
5513
|
+
"agent",
|
|
5514
|
+
"events",
|
|
5219
5515
|
"jewels",
|
|
5220
5516
|
"settings",
|
|
5221
5517
|
"email",
|