@madewithremy/admin 0.1.30 → 0.1.32
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 +410 -25
- package/dist/index.js +110 -4
- package/dist/index.js.map +1 -1
- package/dist/prod.js +754 -118
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -36,10 +36,10 @@ function fatal(message) {
|
|
|
36
36
|
throw new CliError(message);
|
|
37
37
|
}
|
|
38
38
|
var AdminApiError = class extends Error {
|
|
39
|
-
constructor(method,
|
|
40
|
-
super(`API ${method} ${
|
|
39
|
+
constructor(method, path8, status2, body) {
|
|
40
|
+
super(`API ${method} ${path8} returned ${status2}: ${JSON.stringify(body)}`);
|
|
41
41
|
this.method = method;
|
|
42
|
-
this.path =
|
|
42
|
+
this.path = path8;
|
|
43
43
|
this.status = status2;
|
|
44
44
|
this.body = body;
|
|
45
45
|
this.name = "AdminApiError";
|
|
@@ -1036,6 +1036,13 @@ function summarizeRelease(release) {
|
|
|
1036
1036
|
publishedAt: release.publishedAt ?? null,
|
|
1037
1037
|
...release.previewUrl ? { previewUrl: release.previewUrl } : {}
|
|
1038
1038
|
};
|
|
1039
|
+
const readers = (release.dataSources ?? []).filter((d) => d.reader);
|
|
1040
|
+
if (readers.length > 0 && release.status === "live") {
|
|
1041
|
+
summary2.readers = readers.map((d) => ({
|
|
1042
|
+
slug: d.slug,
|
|
1043
|
+
unread: d.readerUnread ?? 0
|
|
1044
|
+
}));
|
|
1045
|
+
}
|
|
1039
1046
|
if (release.status !== "failed") {
|
|
1040
1047
|
return summary2;
|
|
1041
1048
|
}
|
|
@@ -2939,7 +2946,7 @@ var filesSpecs = {
|
|
|
2939
2946
|
usage: "Usage: remy-admin files list"
|
|
2940
2947
|
},
|
|
2941
2948
|
"files rm": {
|
|
2942
|
-
usage: "Usage: remy-admin files rm --store <name> --key <key> [--private]",
|
|
2949
|
+
usage: "Usage: remy-admin files rm --store <name> --key <key> [--public|--private]",
|
|
2943
2950
|
flags: {
|
|
2944
2951
|
...ACCESS_FLAGS,
|
|
2945
2952
|
store: { type: "string" },
|
|
@@ -2950,6 +2957,45 @@ var filesSpecs = {
|
|
|
2950
2957
|
function resolveAccess(a) {
|
|
2951
2958
|
return a.bool("private") ? "private" : "public";
|
|
2952
2959
|
}
|
|
2960
|
+
async function readAccess(ctx, a, key) {
|
|
2961
|
+
if (a.bool("private")) {
|
|
2962
|
+
return "private";
|
|
2963
|
+
}
|
|
2964
|
+
if (a.bool("public")) {
|
|
2965
|
+
return "public";
|
|
2966
|
+
}
|
|
2967
|
+
const store = storeOf(a);
|
|
2968
|
+
const { stores } = await summary(ctx);
|
|
2969
|
+
const held = stores.filter((s) => s.store === store && s.objectCount > 0);
|
|
2970
|
+
if (held.length === 1) {
|
|
2971
|
+
return held[0].access;
|
|
2972
|
+
}
|
|
2973
|
+
if (held.length === 0) {
|
|
2974
|
+
if (key === void 0) {
|
|
2975
|
+
return "public";
|
|
2976
|
+
}
|
|
2977
|
+
fatal(`Store "${store}" holds no objects, public or private.`);
|
|
2978
|
+
}
|
|
2979
|
+
if (key === void 0) {
|
|
2980
|
+
const counts = held.map((s) => `${s.objectCount.toLocaleString()} ${s.access}`).join(", ");
|
|
2981
|
+
fatal(
|
|
2982
|
+
`Store "${store}" holds objects in both halves (${counts}): pass --public or --private.`
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2985
|
+
for (const access of ["private", "public"]) {
|
|
2986
|
+
try {
|
|
2987
|
+
await stat(ctx, { store, access, key });
|
|
2988
|
+
return access;
|
|
2989
|
+
} catch (err) {
|
|
2990
|
+
if (!(err instanceof AdminApiError) || err.status !== 404) {
|
|
2991
|
+
throw err;
|
|
2992
|
+
}
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
fatal(
|
|
2996
|
+
`No object "${key}" in store "${store}" (looked in its private and public halves).`
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2953
2999
|
function storeOf(a) {
|
|
2954
3000
|
return a.str("store") || DEFAULT_STORE;
|
|
2955
3001
|
}
|
|
@@ -2960,8 +3006,8 @@ function requireKeyFlag(a) {
|
|
|
2960
3006
|
}
|
|
2961
3007
|
return key;
|
|
2962
3008
|
}
|
|
2963
|
-
function refOf(a, key) {
|
|
2964
|
-
return { store: storeOf(a), access:
|
|
3009
|
+
async function refOf(ctx, a, key) {
|
|
3010
|
+
return { store: storeOf(a), access: await readAccess(ctx, a, key), key };
|
|
2965
3011
|
}
|
|
2966
3012
|
async function filesPut(ctx, a) {
|
|
2967
3013
|
const file = a.req("file");
|
|
@@ -2987,7 +3033,7 @@ async function filesPut(ctx, a) {
|
|
|
2987
3033
|
}
|
|
2988
3034
|
async function filesGet(ctx, a) {
|
|
2989
3035
|
const key = a.req("key");
|
|
2990
|
-
const ref = refOf(a, key);
|
|
3036
|
+
const ref = await refOf(ctx, a, key);
|
|
2991
3037
|
const { url } = await sign(
|
|
2992
3038
|
ctx,
|
|
2993
3039
|
ref,
|
|
@@ -2997,7 +3043,9 @@ async function filesGet(ctx, a) {
|
|
|
2997
3043
|
const abs = path2.isAbsolute(outPath) ? outPath : path2.join(WORKSPACE_DIR, outPath);
|
|
2998
3044
|
const res = await fetch(url);
|
|
2999
3045
|
if (!res.ok || !res.body) {
|
|
3000
|
-
fatal(
|
|
3046
|
+
fatal(
|
|
3047
|
+
res.status === 403 || res.status === 404 ? `No object "${key}" in the ${ref.access} half of store "${ref.store}" (download answered ${res.status}).` : `Download failed: ${res.status} ${res.statusText}`
|
|
3048
|
+
);
|
|
3001
3049
|
}
|
|
3002
3050
|
await pipeline(
|
|
3003
3051
|
Readable.fromWeb(res.body),
|
|
@@ -3006,16 +3054,18 @@ async function filesGet(ctx, a) {
|
|
|
3006
3054
|
out({ path: abs, size: fs2.statSync(abs).size });
|
|
3007
3055
|
}
|
|
3008
3056
|
async function filesSign(ctx, a) {
|
|
3009
|
-
out(
|
|
3057
|
+
out(
|
|
3058
|
+
await sign(ctx, await refOf(ctx, a, requireKeyFlag(a)), a.num("ttl"))
|
|
3059
|
+
);
|
|
3010
3060
|
}
|
|
3011
3061
|
async function filesStat(ctx, a) {
|
|
3012
|
-
out(await stat(ctx, refOf(a, requireKeyFlag(a))));
|
|
3062
|
+
out(await stat(ctx, await refOf(ctx, a, requireKeyFlag(a))));
|
|
3013
3063
|
}
|
|
3014
3064
|
async function filesLs(ctx, a) {
|
|
3015
3065
|
out(
|
|
3016
3066
|
await ls(ctx, {
|
|
3017
3067
|
store: storeOf(a),
|
|
3018
|
-
access:
|
|
3068
|
+
access: await readAccess(ctx, a),
|
|
3019
3069
|
prefix: a.str("prefix"),
|
|
3020
3070
|
q: a.str("q"),
|
|
3021
3071
|
cursor: a.str("cursor"),
|
|
@@ -3035,7 +3085,13 @@ async function filesRm(ctx, a) {
|
|
|
3035
3085
|
if (!key) {
|
|
3036
3086
|
fatal("--key is required.");
|
|
3037
3087
|
}
|
|
3038
|
-
out(
|
|
3088
|
+
out(
|
|
3089
|
+
await remove(ctx, {
|
|
3090
|
+
store,
|
|
3091
|
+
access: await readAccess(ctx, a, key),
|
|
3092
|
+
key
|
|
3093
|
+
})
|
|
3094
|
+
);
|
|
3039
3095
|
}
|
|
3040
3096
|
var filesHandlers = {
|
|
3041
3097
|
"files put": filesPut,
|
|
@@ -3059,12 +3115,12 @@ Subcommands:
|
|
|
3059
3115
|
|
|
3060
3116
|
Usage:
|
|
3061
3117
|
remy-admin files put [--public|--private] [--store <name>] [--key <key>] [--cache-control <value>] <file>
|
|
3062
|
-
remy-admin files get [--private] [--store <name>] [--out <path>] <key>
|
|
3063
|
-
remy-admin files sign [--private] [--store <name>] [--ttl <seconds>] --key <key>
|
|
3064
|
-
remy-admin files stat [--private] [--store <name>] --key <key>
|
|
3065
|
-
remy-admin files ls [--private] [--store <name>] [--prefix <prefix>] [--q <substring>] [--limit <n>]
|
|
3118
|
+
remy-admin files get [--public|--private] [--store <name>] [--out <path>] <key>
|
|
3119
|
+
remy-admin files sign [--public|--private] [--store <name>] [--ttl <seconds>] --key <key>
|
|
3120
|
+
remy-admin files stat [--public|--private] [--store <name>] --key <key>
|
|
3121
|
+
remy-admin files ls [--public|--private] [--store <name>] [--prefix <prefix>] [--q <substring>] [--limit <n>]
|
|
3066
3122
|
remy-admin files list
|
|
3067
|
-
remy-admin files rm --store <name> --key <key> [--private]
|
|
3123
|
+
remy-admin files rm --store <name> --key <key> [--public|--private]
|
|
3068
3124
|
|
|
3069
3125
|
Examples:
|
|
3070
3126
|
remy-admin files put --public ./hero.jpg
|
|
@@ -3074,7 +3130,10 @@ Examples:
|
|
|
3074
3130
|
remy-admin files rm --store handoff --key <key> --private
|
|
3075
3131
|
|
|
3076
3132
|
Notes:
|
|
3077
|
-
- Defaults to
|
|
3133
|
+
- Defaults to the store named 'assets'. put writes public unless --private.
|
|
3134
|
+
Reads (get, sign, stat, ls, rm) without --public or --private use the half
|
|
3135
|
+
of the store that holds objects; in a store holding both, a key is found
|
|
3136
|
+
wherever it is, and ls asks which.
|
|
3078
3137
|
- Handing someone a large or sensitive file? Use a PRIVATE store + \`sign\`:
|
|
3079
3138
|
the link is unguessable, expires (--ttl seconds, max 7 days), and the object
|
|
3080
3139
|
stays deletable with \`rm\`. Never use the account media CDN
|
|
@@ -3092,8 +3151,8 @@ Notes:
|
|
|
3092
3151
|
- Public image URLs accept transform params, e.g. ?w=400&fit=cover.`;
|
|
3093
3152
|
|
|
3094
3153
|
// src/commands/dataSources.ts
|
|
3095
|
-
import
|
|
3096
|
-
import
|
|
3154
|
+
import fs6 from "fs";
|
|
3155
|
+
import path6 from "path";
|
|
3097
3156
|
|
|
3098
3157
|
// src/ops/dataSources.ts
|
|
3099
3158
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -3111,7 +3170,7 @@ var summarize = (d) => ({
|
|
|
3111
3170
|
...d.errorMessage ? { error: d.errorMessage } : {}
|
|
3112
3171
|
});
|
|
3113
3172
|
async function addDocument(ctx, params) {
|
|
3114
|
-
const { slug, filename, content, metadata, onProgress } = params;
|
|
3173
|
+
const { slug, filename, content, metadata, entities: entities2, onProgress } = params;
|
|
3115
3174
|
const contentHash = createHash2("sha256").update(content).digest("hex");
|
|
3116
3175
|
const token = await call(
|
|
3117
3176
|
ctx,
|
|
@@ -3121,7 +3180,8 @@ async function addDocument(ctx, params) {
|
|
|
3121
3180
|
slug,
|
|
3122
3181
|
filename,
|
|
3123
3182
|
contentHash,
|
|
3124
|
-
...metadata ? { metadata } : {}
|
|
3183
|
+
...metadata ? { metadata } : {},
|
|
3184
|
+
...entities2 ? { entities: entities2 } : {}
|
|
3125
3185
|
}
|
|
3126
3186
|
);
|
|
3127
3187
|
if (token.alreadyCurrent) {
|
|
@@ -3147,7 +3207,8 @@ async function addDocument(ctx, params) {
|
|
|
3147
3207
|
contentHash,
|
|
3148
3208
|
contentType: token.contentType,
|
|
3149
3209
|
size: content.length,
|
|
3150
|
-
...metadata ? { metadata } : {}
|
|
3210
|
+
...metadata ? { metadata } : {},
|
|
3211
|
+
...entities2 ? { entities: entities2 } : {}
|
|
3151
3212
|
}
|
|
3152
3213
|
);
|
|
3153
3214
|
return {
|
|
@@ -3324,6 +3385,35 @@ function count(ctx, params) {
|
|
|
3324
3385
|
...candidate ? { candidate: true } : {}
|
|
3325
3386
|
});
|
|
3326
3387
|
}
|
|
3388
|
+
function entities(ctx, params) {
|
|
3389
|
+
const { slug, filter, limit, candidate } = params;
|
|
3390
|
+
return call(
|
|
3391
|
+
ctx,
|
|
3392
|
+
"POST",
|
|
3393
|
+
`${base(ctx.appId)}/entities`,
|
|
3394
|
+
{
|
|
3395
|
+
slug,
|
|
3396
|
+
...filter ? { filter } : {},
|
|
3397
|
+
...limit !== void 0 ? { limit } : {},
|
|
3398
|
+
...candidate ? { candidate: true } : {}
|
|
3399
|
+
}
|
|
3400
|
+
);
|
|
3401
|
+
}
|
|
3402
|
+
function passages(ctx, params) {
|
|
3403
|
+
const { slug, filter, cursor, limit, candidate } = params;
|
|
3404
|
+
return call(
|
|
3405
|
+
ctx,
|
|
3406
|
+
"POST",
|
|
3407
|
+
`${base(ctx.appId)}/passages`,
|
|
3408
|
+
{
|
|
3409
|
+
slug,
|
|
3410
|
+
...filter ? { filter } : {},
|
|
3411
|
+
...cursor ? { cursor } : {},
|
|
3412
|
+
...limit !== void 0 ? { limit } : {},
|
|
3413
|
+
...candidate ? { candidate: true } : {}
|
|
3414
|
+
}
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3327
3417
|
function configGet(ctx, slug) {
|
|
3328
3418
|
return call(
|
|
3329
3419
|
ctx,
|
|
@@ -3485,6 +3575,67 @@ function deleteSource(ctx, slug) {
|
|
|
3485
3575
|
{ slug }
|
|
3486
3576
|
);
|
|
3487
3577
|
}
|
|
3578
|
+
var DICTIONARY_PAGE_ENTRIES = 1e4;
|
|
3579
|
+
var DICTIONARY_PAGE_BYTES = 4e6;
|
|
3580
|
+
async function pushDictionary(ctx, params) {
|
|
3581
|
+
const { slug, entries } = params;
|
|
3582
|
+
const stage = await call(
|
|
3583
|
+
ctx,
|
|
3584
|
+
"POST",
|
|
3585
|
+
`${base(ctx.appId)}/dictionary/stage`,
|
|
3586
|
+
{
|
|
3587
|
+
slug,
|
|
3588
|
+
...params.linkerVersion !== void 0 ? { linkerVersion: params.linkerVersion } : {}
|
|
3589
|
+
}
|
|
3590
|
+
);
|
|
3591
|
+
const perPage = Math.min(
|
|
3592
|
+
DICTIONARY_PAGE_ENTRIES,
|
|
3593
|
+
stage.pageMaxEntries || DICTIONARY_PAGE_ENTRIES
|
|
3594
|
+
);
|
|
3595
|
+
let pages2 = 0;
|
|
3596
|
+
let start2 = 0;
|
|
3597
|
+
while (start2 < entries.length) {
|
|
3598
|
+
let end = start2;
|
|
3599
|
+
let bytes = 0;
|
|
3600
|
+
while (end < entries.length && end - start2 < perPage) {
|
|
3601
|
+
const size = JSON.stringify(entries[end]).length;
|
|
3602
|
+
if (end > start2 && bytes + size > DICTIONARY_PAGE_BYTES) {
|
|
3603
|
+
break;
|
|
3604
|
+
}
|
|
3605
|
+
bytes += size;
|
|
3606
|
+
end += 1;
|
|
3607
|
+
}
|
|
3608
|
+
await call(ctx, "POST", `${base(ctx.appId)}/dictionary/page`, {
|
|
3609
|
+
slug,
|
|
3610
|
+
stageId: stage.stageId,
|
|
3611
|
+
page: pages2,
|
|
3612
|
+
entries: entries.slice(start2, end)
|
|
3613
|
+
});
|
|
3614
|
+
pages2 += 1;
|
|
3615
|
+
start2 = end;
|
|
3616
|
+
params.onProgress?.(start2, entries.length);
|
|
3617
|
+
}
|
|
3618
|
+
return call(
|
|
3619
|
+
ctx,
|
|
3620
|
+
"POST",
|
|
3621
|
+
`${base(ctx.appId)}/dictionary/commit`,
|
|
3622
|
+
{ slug, stageId: stage.stageId, pages: pages2 }
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
function dictionary(ctx, params) {
|
|
3626
|
+
return call(
|
|
3627
|
+
ctx,
|
|
3628
|
+
"POST",
|
|
3629
|
+
`${base(ctx.appId)}/dictionary`,
|
|
3630
|
+
{ slug: params.slug }
|
|
3631
|
+
);
|
|
3632
|
+
}
|
|
3633
|
+
function link(ctx, params) {
|
|
3634
|
+
return call(ctx, "POST", `${base(ctx.appId)}/link`, {
|
|
3635
|
+
slug: params.slug,
|
|
3636
|
+
text: params.text
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3488
3639
|
|
|
3489
3640
|
// src/ops/dataSourceJobs.ts
|
|
3490
3641
|
var DEFAULT_WAIT_TIMEOUT_MS2 = 60 * 60 * 1e3;
|
|
@@ -3643,6 +3794,39 @@ function describeProgress(job, start2) {
|
|
|
3643
3794
|
// src/commands/_shared/dataSources.ts
|
|
3644
3795
|
var DEFAULT_SOURCE = "default";
|
|
3645
3796
|
var sourceOf = (a) => a.str("source") || DEFAULT_SOURCE;
|
|
3797
|
+
function parseKeyValuePairs(raw, flag) {
|
|
3798
|
+
if (raw === void 0) {
|
|
3799
|
+
return void 0;
|
|
3800
|
+
}
|
|
3801
|
+
const pairs2 = {};
|
|
3802
|
+
for (const part of raw.split(",")) {
|
|
3803
|
+
const trimmed = part.trim();
|
|
3804
|
+
if (!trimmed) {
|
|
3805
|
+
continue;
|
|
3806
|
+
}
|
|
3807
|
+
const eq = trimmed.indexOf("=");
|
|
3808
|
+
if (eq <= 0) {
|
|
3809
|
+
fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
|
|
3810
|
+
}
|
|
3811
|
+
const key = trimmed.slice(0, eq).trim();
|
|
3812
|
+
const value = trimmed.slice(eq + 1).trim();
|
|
3813
|
+
pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
|
|
3814
|
+
}
|
|
3815
|
+
if (Object.keys(pairs2).length === 0) {
|
|
3816
|
+
fatal(`--${flag} needs at least one key=value pair.`);
|
|
3817
|
+
}
|
|
3818
|
+
return pairs2;
|
|
3819
|
+
}
|
|
3820
|
+
function documentSelectorOf(raw) {
|
|
3821
|
+
if (raw.trimStart().startsWith("{")) {
|
|
3822
|
+
try {
|
|
3823
|
+
return JSON.parse(raw);
|
|
3824
|
+
} catch (err) {
|
|
3825
|
+
fatal(`--filter is not valid JSON: ${err.message}`);
|
|
3826
|
+
}
|
|
3827
|
+
}
|
|
3828
|
+
return { metadata: parseKeyValuePairs(raw, "filter") };
|
|
3829
|
+
}
|
|
3646
3830
|
var timeoutOf = (a) => {
|
|
3647
3831
|
const seconds = a.str("timeout");
|
|
3648
3832
|
return seconds ? { timeoutMs: Number(seconds) * 1e3 } : {};
|
|
@@ -4658,9 +4842,118 @@ Retrieval evals:
|
|
|
4658
4842
|
with --candidate, compare, promote or drop.
|
|
4659
4843
|
`;
|
|
4660
4844
|
|
|
4661
|
-
// src/commands/
|
|
4845
|
+
// src/commands/dataSourceDictionary.ts
|
|
4662
4846
|
import fs4 from "fs";
|
|
4663
4847
|
import path4 from "path";
|
|
4848
|
+
var dataSourceDictionarySpecs = {
|
|
4849
|
+
"datasources dictionary push": {
|
|
4850
|
+
usage: "Usage: remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>",
|
|
4851
|
+
positionals: [{ name: "file", required: true }],
|
|
4852
|
+
flags: {
|
|
4853
|
+
source: { type: "string" },
|
|
4854
|
+
"linker-version": { type: "number", min: 1 }
|
|
4855
|
+
}
|
|
4856
|
+
},
|
|
4857
|
+
"datasources dictionary status": {
|
|
4858
|
+
usage: "Usage: remy-admin datasources dictionary status [--source <slug>]",
|
|
4859
|
+
flags: {
|
|
4860
|
+
source: { type: "string" }
|
|
4861
|
+
}
|
|
4862
|
+
},
|
|
4863
|
+
"datasources link": {
|
|
4864
|
+
usage: "Usage: remy-admin datasources link [--source <slug>] <text>",
|
|
4865
|
+
positionals: [{ name: "text", required: true }],
|
|
4866
|
+
flags: {
|
|
4867
|
+
source: { type: "string" }
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
};
|
|
4871
|
+
function readEntries(file) {
|
|
4872
|
+
const abs = path4.isAbsolute(file) ? file : path4.join(WORKSPACE_DIR, file);
|
|
4873
|
+
let content;
|
|
4874
|
+
try {
|
|
4875
|
+
content = fs4.readFileSync(abs, "utf8");
|
|
4876
|
+
} catch (err) {
|
|
4877
|
+
fatal(`Could not read "${file}": ${err.message}`);
|
|
4878
|
+
}
|
|
4879
|
+
const entries = [];
|
|
4880
|
+
content.split("\n").forEach((line, i) => {
|
|
4881
|
+
const trimmed = line.trim();
|
|
4882
|
+
if (!trimmed) {
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
let parsed;
|
|
4886
|
+
try {
|
|
4887
|
+
parsed = JSON.parse(trimmed);
|
|
4888
|
+
} catch {
|
|
4889
|
+
fatal(`${file}:${i + 1} is not valid JSON.`);
|
|
4890
|
+
}
|
|
4891
|
+
if (typeof parsed?.id !== "string" || !Array.isArray(parsed?.names) || !parsed.names.every((name) => typeof name === "string")) {
|
|
4892
|
+
fatal(`${file}:${i + 1} needs { "id": "...", "names": ["...", ...] }.`);
|
|
4893
|
+
}
|
|
4894
|
+
entries.push({ id: parsed.id, names: parsed.names });
|
|
4895
|
+
});
|
|
4896
|
+
if (entries.length === 0) {
|
|
4897
|
+
fatal(`"${file}" has no entries.`);
|
|
4898
|
+
}
|
|
4899
|
+
return entries;
|
|
4900
|
+
}
|
|
4901
|
+
async function dictionaryPush(ctx, a) {
|
|
4902
|
+
const entries = readEntries(a.req("file"));
|
|
4903
|
+
const linkerVersion = a.num("linker-version");
|
|
4904
|
+
out(
|
|
4905
|
+
await pushDictionary(ctx, {
|
|
4906
|
+
slug: sourceOf(a),
|
|
4907
|
+
entries,
|
|
4908
|
+
...linkerVersion !== void 0 ? { linkerVersion } : {},
|
|
4909
|
+
onProgress: (sent, total) => progress(`pushing ${sent.toLocaleString()}/${total.toLocaleString()}\u2026`)
|
|
4910
|
+
})
|
|
4911
|
+
);
|
|
4912
|
+
}
|
|
4913
|
+
async function dictionaryStatus(ctx, a) {
|
|
4914
|
+
out(await dictionary(ctx, { slug: sourceOf(a) }));
|
|
4915
|
+
}
|
|
4916
|
+
async function link2(ctx, a) {
|
|
4917
|
+
out(await link(ctx, { slug: sourceOf(a), text: a.req("text") }));
|
|
4918
|
+
}
|
|
4919
|
+
var dataSourceDictionaryHandlers = {
|
|
4920
|
+
"datasources dictionary push": dictionaryPush,
|
|
4921
|
+
"datasources dictionary status": dictionaryStatus,
|
|
4922
|
+
"datasources link": link2
|
|
4923
|
+
};
|
|
4924
|
+
var dataSourceDictionaryHelp = `
|
|
4925
|
+
Dictionary:
|
|
4926
|
+
remy-admin datasources dictionary push [--source <slug>] [--linker-version <n>] <file.jsonl>
|
|
4927
|
+
remy-admin datasources dictionary status [--source <slug>]
|
|
4928
|
+
remy-admin datasources link [--source <slug>] <text>
|
|
4929
|
+
|
|
4930
|
+
A dictionary is your own entity ids and the names each goes by, a
|
|
4931
|
+
projection of your tables: one JSONL line per entry,
|
|
4932
|
+
{"id": "P-101A", "names": ["P-101A", "Main Feed Pump"]}. Push it whole; it
|
|
4933
|
+
replaces the source's dictionary. From then on every passage carries the
|
|
4934
|
+
ids whose names its text mentions, beside the ones its document asserts,
|
|
4935
|
+
so --entities on search and count, and \`datasources entities\`, find a tag
|
|
4936
|
+
wherever it's written. Passages indexed before are re-tagged in the
|
|
4937
|
+
background (a relink; \`dictionary status\` shows it), with nothing
|
|
4938
|
+
re-embedded. An unchanged dictionary commits nothing.
|
|
4939
|
+
|
|
4940
|
+
Matching ignores case and a tag's separators (P-101A, P101A, p 101a) and
|
|
4941
|
+
an inch or foot mark after a size (8"-P-1057-A1A is 8-P-1057-A1A), takes
|
|
4942
|
+
whole words only (P-101AB is not P-101A), and prefers the longest name; a
|
|
4943
|
+
name two ids claim tags both. A name needs three letters or digits (two in
|
|
4944
|
+
Chinese, Japanese or Korean). The push reports ambiguous names, the most
|
|
4945
|
+
claimed (each with one name as pushed), and names that can't match their
|
|
4946
|
+
own text as written (\`unmatchable\`). Up to 250,000 entries, 16 names
|
|
4947
|
+
each, a million names.
|
|
4948
|
+
|
|
4949
|
+
\`link\` shows what the current dictionary finds in any text: check one
|
|
4950
|
+
there before relying on it. --linker-version moves a dictionary to a newer
|
|
4951
|
+
matching rule; without it a push keeps the one its dictionary has.
|
|
4952
|
+
`;
|
|
4953
|
+
|
|
4954
|
+
// src/commands/dataSourceJobs.ts
|
|
4955
|
+
import fs5 from "fs";
|
|
4956
|
+
import path5 from "path";
|
|
4664
4957
|
var dataSourceJobsSpecs = {
|
|
4665
4958
|
"datasources jobs start": {
|
|
4666
4959
|
usage: "Usage: remy-admin datasources jobs start [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --manifest <file.jsonl>) [--limit <n>] [--budget <dollars>] [--concurrency <n>] [--priority] [--approve] [--wait] [--timeout <sec>]",
|
|
@@ -4738,10 +5031,10 @@ async function jobsStart(ctx, a) {
|
|
|
4738
5031
|
}
|
|
4739
5032
|
let source;
|
|
4740
5033
|
if (manifest) {
|
|
4741
|
-
const abs =
|
|
5034
|
+
const abs = path5.isAbsolute(manifest) ? manifest : path5.join(WORKSPACE_DIR, manifest);
|
|
4742
5035
|
let content;
|
|
4743
5036
|
try {
|
|
4744
|
-
content =
|
|
5037
|
+
content = fs5.readFileSync(abs);
|
|
4745
5038
|
} catch (err) {
|
|
4746
5039
|
fatal(`Could not read manifest "${manifest}": ${err.message}`);
|
|
4747
5040
|
}
|
|
@@ -4867,12 +5160,13 @@ Bulk ingestion (jobs):
|
|
|
4867
5160
|
A job loads a whole corpus at once. It reads either every object under a
|
|
4868
5161
|
prefix of one of the app's file stores (\`files put\` fills one; an app can
|
|
4869
5162
|
write one at runtime) or a manifest you upload: JSONL, one line per document,
|
|
4870
|
-
{"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026}
|
|
4871
|
-
|
|
5163
|
+
{"url": "https://\u2026", "filename": "x.pdf", "externalId": "\u2026", "metadata": {\u2026},
|
|
5164
|
+
"entities": ["\u2026"]}. Everything but the URL is optional; it must be public.
|
|
4872
5165
|
|
|
4873
|
-
Nothing
|
|
4874
|
-
sample of about a hundred documents, and writes a plan:
|
|
4875
|
-
cost per stage (embedding, extraction, contextual
|
|
5166
|
+
Nothing past the sample runs until you say so. The platform counts the
|
|
5167
|
+
source, reads a sample of about a hundred documents, and writes a plan:
|
|
5168
|
+
documents, chunks, cost per stage (embedding, extraction, contextual, and
|
|
5169
|
+
the mapper's own model calls, priced from the sample's) at today's rates, storage,
|
|
4876
5170
|
whether it fits where the source lives, and a duration. \`jobs status\` shows
|
|
4877
5171
|
it; \`jobs approve\` starts the run. Two gates: the corpus has to fit the
|
|
4878
5172
|
source's placement (a shared-pool source over the per-source cap answers
|
|
@@ -4954,7 +5248,8 @@ function mapTest(ctx, params) {
|
|
|
4954
5248
|
...selectionBody(params.selection),
|
|
4955
5249
|
...params.keys ? { keys: params.keys } : {},
|
|
4956
5250
|
...params.limit !== void 0 ? { limit: params.limit } : {},
|
|
4957
|
-
...params.dev ? { dev: true } : {}
|
|
5251
|
+
...params.dev ? { dev: true } : {},
|
|
5252
|
+
...params.releaseId ? { releaseId: params.releaseId } : {}
|
|
4958
5253
|
}
|
|
4959
5254
|
);
|
|
4960
5255
|
}
|
|
@@ -4982,6 +5277,7 @@ function quarantine(ctx, params) {
|
|
|
4982
5277
|
"GET",
|
|
4983
5278
|
`${base5(ctx.appId)}/jobs/${params.id}/quarantine${qs({
|
|
4984
5279
|
...params.kind ? { kind: params.kind } : {},
|
|
5280
|
+
...params.stage ? { stage: params.stage } : {},
|
|
4985
5281
|
...params.reason ? { reason: params.reason } : {},
|
|
4986
5282
|
...params.cursor ? { cursor: params.cursor } : {},
|
|
4987
5283
|
...params.limit !== void 0 ? { limit: params.limit } : {}
|
|
@@ -5019,12 +5315,13 @@ var dataSourceMappersSpecs = {
|
|
|
5019
5315
|
}
|
|
5020
5316
|
},
|
|
5021
5317
|
"datasources map test": {
|
|
5022
|
-
usage: "Usage: remy-admin datasources map test [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --connector) [--keys k1,k2,...] [--limit <n>] [--dev] [--full]",
|
|
5318
|
+
usage: "Usage: remy-admin datasources map test [--source <slug>] (--store <name> [--access <private|public>] [--prefix <p>] | --connector) [--keys k1,k2,...] [--limit <n>] [--dev [--release <dev-release-id>]] [--full]",
|
|
5023
5319
|
flags: {
|
|
5024
5320
|
source: { type: "string" },
|
|
5025
5321
|
...SELECTION_FLAGS,
|
|
5026
5322
|
limit: { type: "number", min: 1 },
|
|
5027
5323
|
dev: { type: "boolean" },
|
|
5324
|
+
release: { type: "string" },
|
|
5028
5325
|
full: { type: "boolean" }
|
|
5029
5326
|
},
|
|
5030
5327
|
requireAnyOf: {
|
|
@@ -5050,10 +5347,11 @@ var dataSourceMappersSpecs = {
|
|
|
5050
5347
|
}
|
|
5051
5348
|
},
|
|
5052
5349
|
"datasources jobs quarantine": {
|
|
5053
|
-
usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]",
|
|
5350
|
+
usage: "Usage: remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]",
|
|
5054
5351
|
positionals: [{ name: "id", required: true }],
|
|
5055
5352
|
flags: {
|
|
5056
5353
|
kind: { type: "string" },
|
|
5354
|
+
stage: { type: "string" },
|
|
5057
5355
|
reason: { type: "string" },
|
|
5058
5356
|
cursor: { type: "string" },
|
|
5059
5357
|
limit: { type: "number", min: 1 }
|
|
@@ -5135,7 +5433,8 @@ async function mapTest2(ctx, a) {
|
|
|
5135
5433
|
selection: selectionOf(a),
|
|
5136
5434
|
...keysOf(a) ? { keys: keysOf(a) } : {},
|
|
5137
5435
|
...limit !== void 0 ? { limit } : {},
|
|
5138
|
-
...a.bool("dev") ? { dev: true } : {}
|
|
5436
|
+
...a.bool("dev") ? { dev: true } : {},
|
|
5437
|
+
...a.str("release") ? { releaseId: a.str("release") } : {}
|
|
5139
5438
|
});
|
|
5140
5439
|
const results = a.bool("full") ? result.results : result.results.map(
|
|
5141
5440
|
(r) => r.outcome.kind === "documents" ? {
|
|
@@ -5206,18 +5505,32 @@ async function quarantine2(ctx, a) {
|
|
|
5206
5505
|
const id = a.req("id");
|
|
5207
5506
|
const limit = a.num("limit");
|
|
5208
5507
|
const kind = kindOf(a);
|
|
5508
|
+
const stage = a.str("stage");
|
|
5509
|
+
if (stage !== void 0 && stage !== "map" && stage !== "read") {
|
|
5510
|
+
fatal(`--stage must be "map" or "read" (got "${stage}").`);
|
|
5511
|
+
}
|
|
5209
5512
|
const reason = a.str("reason");
|
|
5210
5513
|
const cursor = a.str("cursor");
|
|
5211
5514
|
const result = await quarantine(ctx, {
|
|
5212
5515
|
id,
|
|
5213
5516
|
...kind ? { kind } : {},
|
|
5517
|
+
...stage ? { stage } : {},
|
|
5214
5518
|
...reason ? { reason } : {},
|
|
5215
5519
|
...cursor ? { cursor } : {},
|
|
5216
5520
|
...limit !== void 0 ? { limit } : {}
|
|
5217
5521
|
});
|
|
5522
|
+
const { skipped, errors, readErrors } = result.counts;
|
|
5523
|
+
const notes = [
|
|
5524
|
+
...skipped + errors > 0 ? [
|
|
5525
|
+
`Mapping: fix the mapper, push, \`datasources map deploy\`, then \`datasources jobs replay ${id}\` to run these through it again.`
|
|
5526
|
+
] : [],
|
|
5527
|
+
...readErrors > 0 ? [
|
|
5528
|
+
`Reading: fix the reader and publish, then \`datasources reader retry --source ${result.dataSource.slug} --job ${id}\` reads these again.`
|
|
5529
|
+
] : []
|
|
5530
|
+
];
|
|
5218
5531
|
out({
|
|
5219
5532
|
...result,
|
|
5220
|
-
note:
|
|
5533
|
+
note: notes.length === 0 ? "Nothing quarantined: the mapper produced documents (or deletions) for every object, and the reader read every document." : notes.join(" ")
|
|
5221
5534
|
});
|
|
5222
5535
|
}
|
|
5223
5536
|
async function replay2(ctx, a) {
|
|
@@ -5255,10 +5568,10 @@ var dataSourceMappersHandlers = {
|
|
|
5255
5568
|
var dataSourceMappersHelp = `
|
|
5256
5569
|
Mapping raw objects into documents (mappers):
|
|
5257
5570
|
remy-admin datasources inspect [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>]
|
|
5258
|
-
remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev] [--full]
|
|
5571
|
+
remy-admin datasources map test [--source <slug>] (--store <name> [--prefix <p>] | --connector) [--keys k1,k2] [--limit <n>] [--dev [--release <id>]] [--full]
|
|
5259
5572
|
remy-admin datasources map deploy [--source <slug>] [--release <id>]
|
|
5260
5573
|
remy-admin datasources remap [--source <slug>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
|
|
5261
|
-
remy-admin datasources jobs quarantine <id> [--kind skip|error] [--reason <text>] [--cursor <id>] [--limit <n>]
|
|
5574
|
+
remy-admin datasources jobs quarantine <id> [--kind skip|error] [--stage map|read] [--reason <text>] [--cursor <id>] [--limit <n>]
|
|
5262
5575
|
remy-admin datasources jobs replay <id> [--kind skip|error] [--wait] [--timeout <sec>]
|
|
5263
5576
|
|
|
5264
5577
|
A source takes every object it is given as one document. When the file is
|
|
@@ -5299,6 +5612,234 @@ Mapping raw objects into documents (mappers):
|
|
|
5299
5612
|
pauses with pauseReason 'skips' for a look at the quarantine.
|
|
5300
5613
|
`;
|
|
5301
5614
|
|
|
5615
|
+
// src/ops/dataSourceReaders.ts
|
|
5616
|
+
function base6(appId) {
|
|
5617
|
+
return `/_internal/v2/apps/${appId}/datasources`;
|
|
5618
|
+
}
|
|
5619
|
+
function readerTest(ctx, params) {
|
|
5620
|
+
return call(
|
|
5621
|
+
ctx,
|
|
5622
|
+
"POST",
|
|
5623
|
+
`${base6(ctx.appId)}/reader/test`,
|
|
5624
|
+
{
|
|
5625
|
+
slug: params.slug,
|
|
5626
|
+
...params.documents ? { documents: params.documents } : {},
|
|
5627
|
+
...params.limit !== void 0 ? { limit: params.limit } : {},
|
|
5628
|
+
...params.releaseId ? { releaseId: params.releaseId } : {}
|
|
5629
|
+
}
|
|
5630
|
+
);
|
|
5631
|
+
}
|
|
5632
|
+
function readerStatus(ctx, params) {
|
|
5633
|
+
return call(
|
|
5634
|
+
ctx,
|
|
5635
|
+
"POST",
|
|
5636
|
+
`${base6(ctx.appId)}/reader/status`,
|
|
5637
|
+
{ slug: params.slug }
|
|
5638
|
+
);
|
|
5639
|
+
}
|
|
5640
|
+
function readerRetry(ctx, params) {
|
|
5641
|
+
return call(
|
|
5642
|
+
ctx,
|
|
5643
|
+
"POST",
|
|
5644
|
+
`${base6(ctx.appId)}/reader/retry`,
|
|
5645
|
+
{
|
|
5646
|
+
slug: params.slug,
|
|
5647
|
+
...params.jobId ? { jobId: params.jobId } : {}
|
|
5648
|
+
}
|
|
5649
|
+
);
|
|
5650
|
+
}
|
|
5651
|
+
function reread(ctx, params) {
|
|
5652
|
+
return call(
|
|
5653
|
+
ctx,
|
|
5654
|
+
"POST",
|
|
5655
|
+
`${base6(ctx.appId)}/reread`,
|
|
5656
|
+
{
|
|
5657
|
+
slug: params.slug,
|
|
5658
|
+
...params.unread ? { unread: true } : {},
|
|
5659
|
+
...params.filter ? { filter: params.filter } : {},
|
|
5660
|
+
...params.budgetDollars !== void 0 ? { budgetDollars: params.budgetDollars } : {},
|
|
5661
|
+
...params.limit !== void 0 ? { limit: params.limit } : {}
|
|
5662
|
+
}
|
|
5663
|
+
);
|
|
5664
|
+
}
|
|
5665
|
+
|
|
5666
|
+
// src/commands/dataSourceReaders.ts
|
|
5667
|
+
var dataSourceReadersSpecs = {
|
|
5668
|
+
"datasources reader test": {
|
|
5669
|
+
usage: "Usage: remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2,...] [--limit <n>] [--release <dev-release-id>]",
|
|
5670
|
+
flags: {
|
|
5671
|
+
source: { type: "string" },
|
|
5672
|
+
dev: { type: "boolean" },
|
|
5673
|
+
documents: { type: "string" },
|
|
5674
|
+
limit: { type: "number", min: 1 },
|
|
5675
|
+
release: { type: "string" }
|
|
5676
|
+
}
|
|
5677
|
+
},
|
|
5678
|
+
"datasources reader status": {
|
|
5679
|
+
usage: "Usage: remy-admin datasources reader status [--source <slug>]",
|
|
5680
|
+
flags: {
|
|
5681
|
+
source: { type: "string" }
|
|
5682
|
+
}
|
|
5683
|
+
},
|
|
5684
|
+
"datasources reader retry": {
|
|
5685
|
+
usage: "Usage: remy-admin datasources reader retry [--source <slug>] [--job <id>]",
|
|
5686
|
+
flags: {
|
|
5687
|
+
source: { type: "string" },
|
|
5688
|
+
job: { type: "string" }
|
|
5689
|
+
}
|
|
5690
|
+
},
|
|
5691
|
+
"datasources reread": {
|
|
5692
|
+
usage: "Usage: remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v,...|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]",
|
|
5693
|
+
flags: {
|
|
5694
|
+
source: { type: "string" },
|
|
5695
|
+
unread: { type: "boolean" },
|
|
5696
|
+
filter: { type: "string" },
|
|
5697
|
+
budget: { type: "number", min: 0.01 },
|
|
5698
|
+
limit: { type: "number", min: 1 },
|
|
5699
|
+
wait: { type: "boolean" },
|
|
5700
|
+
timeout: { type: "string" }
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
};
|
|
5704
|
+
async function readerTest2(ctx, a) {
|
|
5705
|
+
if (!a.bool("dev")) {
|
|
5706
|
+
fatal(
|
|
5707
|
+
"reader test runs the dev session's reader only: pass --dev. The live reader reads through deliveries and `datasources reread`."
|
|
5708
|
+
);
|
|
5709
|
+
}
|
|
5710
|
+
const slug = sourceOf(a);
|
|
5711
|
+
const raw = a.str("documents");
|
|
5712
|
+
const documents2 = raw?.split(",").map((id) => id.trim()).filter(Boolean);
|
|
5713
|
+
if (raw !== void 0 && documents2?.length === 0) {
|
|
5714
|
+
fatal("--documents needs at least one document id.");
|
|
5715
|
+
}
|
|
5716
|
+
const limit = a.num("limit");
|
|
5717
|
+
const result = await readerTest(ctx, {
|
|
5718
|
+
slug,
|
|
5719
|
+
...documents2 ? { documents: documents2 } : {},
|
|
5720
|
+
...limit !== void 0 ? { limit } : {},
|
|
5721
|
+
...a.str("release") ? { releaseId: a.str("release") } : {}
|
|
5722
|
+
});
|
|
5723
|
+
out({
|
|
5724
|
+
dataSource: slug,
|
|
5725
|
+
...result,
|
|
5726
|
+
note: "Ran the LOCAL reader through the dev session, writing into the dev session's database. Query its tables to check what it wrote; publish to make it the live reader, then `datasources reread --unread` reads what it has not."
|
|
5727
|
+
});
|
|
5728
|
+
if (result.failed > 0) {
|
|
5729
|
+
throw new CliError(
|
|
5730
|
+
`The reader failed on ${result.failed} of ${result.documents.length} document(s).`,
|
|
5731
|
+
EXIT.buildFailed
|
|
5732
|
+
);
|
|
5733
|
+
}
|
|
5734
|
+
}
|
|
5735
|
+
async function readerStatus2(ctx, a) {
|
|
5736
|
+
const slug = sourceOf(a);
|
|
5737
|
+
const result = await readerStatus(ctx, { slug });
|
|
5738
|
+
const { reader, deliveries, documents: documents2 } = result;
|
|
5739
|
+
out({
|
|
5740
|
+
dataSource: slug,
|
|
5741
|
+
...result,
|
|
5742
|
+
note: !reader ? `The live release declares no reader for "${slug}". Declare one in mindstudio.json's dataSources and publish.` : deliveries.parked > 0 ? `${deliveries.parked} delivery(ies) failed six times and parked. Fix the reader and publish, then \`datasources reader retry\`.` : documents2.unread > 0 ? `${documents2.unread.toLocaleString()} live document(s) not read yet. \`datasources reread --unread\` plans reading them.` : "Every live document has been read."
|
|
5743
|
+
});
|
|
5744
|
+
}
|
|
5745
|
+
async function readerRetry2(ctx, a) {
|
|
5746
|
+
const slug = sourceOf(a);
|
|
5747
|
+
const jobId = a.str("job");
|
|
5748
|
+
const result = await readerRetry(ctx, {
|
|
5749
|
+
slug,
|
|
5750
|
+
...jobId ? { jobId } : {}
|
|
5751
|
+
});
|
|
5752
|
+
out({
|
|
5753
|
+
dataSource: slug,
|
|
5754
|
+
...jobId ? { job: jobId } : {},
|
|
5755
|
+
...result,
|
|
5756
|
+
note: result.retried === 0 ? jobId ? `Job ${jobId}'s reader failed on nothing.` : "Nothing parked." : `Queued ${result.retried.toLocaleString()} delivery(ies) again; \`datasources reader status\` shows them drain.`
|
|
5757
|
+
});
|
|
5758
|
+
}
|
|
5759
|
+
async function reread2(ctx, a) {
|
|
5760
|
+
const slug = sourceOf(a);
|
|
5761
|
+
const raw = a.str("filter");
|
|
5762
|
+
const budget = a.num("budget");
|
|
5763
|
+
const limit = a.num("limit");
|
|
5764
|
+
const { job } = await reread(ctx, {
|
|
5765
|
+
slug,
|
|
5766
|
+
...a.bool("unread") ? { unread: true } : {},
|
|
5767
|
+
...raw !== void 0 ? { filter: documentSelectorOf(raw) } : {},
|
|
5768
|
+
...budget !== void 0 ? { budgetDollars: budget } : {},
|
|
5769
|
+
...limit !== void 0 ? { limit } : {}
|
|
5770
|
+
});
|
|
5771
|
+
if (!a.bool("wait")) {
|
|
5772
|
+
out({
|
|
5773
|
+
dataSource: slug,
|
|
5774
|
+
job,
|
|
5775
|
+
note: `Planning: the reread prices reading from the reader's history, or reads a first slice for real to measure it. \`datasources jobs status ${job.id}\` shows the plan; \`datasources jobs approve ${job.id}\` runs it.`
|
|
5776
|
+
});
|
|
5777
|
+
return;
|
|
5778
|
+
}
|
|
5779
|
+
await waitAndReport(ctx, a, job.id, "plan", {
|
|
5780
|
+
dataSource: slug,
|
|
5781
|
+
action: "reread"
|
|
5782
|
+
});
|
|
5783
|
+
}
|
|
5784
|
+
var dataSourceReadersHandlers = {
|
|
5785
|
+
"datasources reader test": readerTest2,
|
|
5786
|
+
"datasources reader status": readerStatus2,
|
|
5787
|
+
"datasources reader retry": readerRetry2,
|
|
5788
|
+
"datasources reread": reread2
|
|
5789
|
+
};
|
|
5790
|
+
var dataSourceReadersHelp = `
|
|
5791
|
+
Reading documents into the app (readers):
|
|
5792
|
+
remy-admin datasources reader test --dev [--source <slug>] [--documents id1,id2] [--limit <n>] [--release <id>]
|
|
5793
|
+
remy-admin datasources reader status [--source <slug>]
|
|
5794
|
+
remy-admin datasources reader retry [--source <slug>] [--job <id>]
|
|
5795
|
+
remy-admin datasources reread [--source <slug>] [--unread] [--filter <k=v|json>] [--budget <dollars>] [--limit <n>] [--wait] [--timeout <sec>]
|
|
5796
|
+
|
|
5797
|
+
A READER is app code the platform runs over every document a source
|
|
5798
|
+
builds, writing what it finds into the app's own tables: the extraction an
|
|
5799
|
+
app would otherwise run from a cron over allDocuments(), with the
|
|
5800
|
+
platform's batching, retries, budgets and pauses. Declare it in
|
|
5801
|
+
mindstudio.json:
|
|
5802
|
+
"dataSources": [{ "slug": "archive", "reader": { "path": "dist/datasources/archive.reader.ts" } }]
|
|
5803
|
+
The file exports defineReader(Archive, { read, removed, cleared?,
|
|
5804
|
+
relinked? }) from @mindstudio-ai/agent. read(document) gets the document
|
|
5805
|
+
(id, sourceId, filename, metadata, entities, url), document.chunks(), its
|
|
5806
|
+
passages as indexed with each one's entities, and document.bytes(), the
|
|
5807
|
+
file it came from; removed(documentId) is called when a document leaves
|
|
5808
|
+
the source; cleared(sourceId) once when the source is deleted; relinked(
|
|
5809
|
+
document) when a new dictionary re-tagged a document already read, for
|
|
5810
|
+
rows derived from its tags. Throwing fails that document, which is
|
|
5811
|
+
retried. A document can be
|
|
5812
|
+
read more than once, so read() replaces the document's rows as a set,
|
|
5813
|
+
keyed by its id, and removed() deletes them. Options on the declaration:
|
|
5814
|
+
timeoutMs (per document, default 60000, max 300000) and concurrency
|
|
5815
|
+
(frames in flight for the source, default 16, max 64).
|
|
5816
|
+
|
|
5817
|
+
The reader always runs against the LIVE release, its code and its
|
|
5818
|
+
database: nothing reads until the app publishes one. From then on every
|
|
5819
|
+
document whose build lands on the active version is read, every removed
|
|
5820
|
+
document delivered as removed, whether it came through add(), a sync or a
|
|
5821
|
+
job. A job over a reader source reads as its last stage: the job stays
|
|
5822
|
+
running until its documents are read, \`jobs status\` says "reading N of M",
|
|
5823
|
+
a document the reader fails on lands in the job's quarantine (stage read),
|
|
5824
|
+
and a reader failing on far more documents than it did at first pauses the
|
|
5825
|
+
job with pauseReason 'reader'.
|
|
5826
|
+
|
|
5827
|
+
The loop: \`reader test --dev\` runs the LOCAL reader through the dev
|
|
5828
|
+
session over real documents, writing into the dev session's database, and
|
|
5829
|
+
prints each outcome; publish, and \`releases wait\` reports each reader's
|
|
5830
|
+
unread documents; \`reread --unread\` reads them. \`reread\` is a job that
|
|
5831
|
+
plans first: it prices reading from what the live reader has done (after a
|
|
5832
|
+
hundred documents), or reads a first slice for real and measures spend,
|
|
5833
|
+
failures and the live database's growth, then waits for \`jobs approve\`.
|
|
5834
|
+
Without --unread it reads every built document again (a changed reader).
|
|
5835
|
+
\`reader status\` shows the reader, its deliveries (pending, in flight,
|
|
5836
|
+
retrying, parked, recent errors), unread documents, the measured cost per
|
|
5837
|
+
document and the live database's size. Deliveries outside jobs retry after
|
|
5838
|
+
1, 5, 15, 60 and 240 minutes, then park; \`reader retry\` sends parked ones
|
|
5839
|
+
again, and \`reader retry --job <id>\` the documents a job's reader failed
|
|
5840
|
+
on.
|
|
5841
|
+
`;
|
|
5842
|
+
|
|
5302
5843
|
// src/commands/dataSources.ts
|
|
5303
5844
|
var INGEST_FLAGS = {
|
|
5304
5845
|
chunking: { type: "string" },
|
|
@@ -5329,11 +5870,12 @@ var CONFIG_FLAGS = {
|
|
|
5329
5870
|
};
|
|
5330
5871
|
var dataSourcesSpecs = {
|
|
5331
5872
|
"datasources add": {
|
|
5332
|
-
usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
|
|
5873
|
+
usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>",
|
|
5333
5874
|
positionals: [{ name: "file", required: true, variadic: true }],
|
|
5334
5875
|
flags: {
|
|
5335
5876
|
source: { type: "string" },
|
|
5336
5877
|
metadata: { type: "string" },
|
|
5878
|
+
entities: { type: "string" },
|
|
5337
5879
|
wait: { type: "boolean" },
|
|
5338
5880
|
timeout: { type: "string" }
|
|
5339
5881
|
}
|
|
@@ -5362,7 +5904,7 @@ var dataSourcesSpecs = {
|
|
|
5362
5904
|
}
|
|
5363
5905
|
},
|
|
5364
5906
|
"datasources search": {
|
|
5365
|
-
usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
|
|
5907
|
+
usage: "Usage: remy-admin datasources search [--source <slug>] [--top-k <n>] [--mode <hybrid|semantic|lexical>] [--filter <k=v,...|json>] [--entities <id,...>] [--phrase <text>] [--contains <words>] [--max-per-document <n>] [--rerank <true|false>] [--hybrid <true|false>] [--highlight] [--candidate] <query>",
|
|
5366
5908
|
positionals: [{ name: "query", required: true }],
|
|
5367
5909
|
flags: {
|
|
5368
5910
|
source: { type: "string" },
|
|
@@ -5372,6 +5914,7 @@ var dataSourcesSpecs = {
|
|
|
5372
5914
|
hybrid: { type: "string" },
|
|
5373
5915
|
mode: { type: "string" },
|
|
5374
5916
|
filter: { type: "string" },
|
|
5917
|
+
entities: { type: "string" },
|
|
5375
5918
|
phrase: { type: "string" },
|
|
5376
5919
|
contains: { type: "string" },
|
|
5377
5920
|
"max-per-document": { type: "number", min: 1 },
|
|
@@ -5398,12 +5941,38 @@ var dataSourcesSpecs = {
|
|
|
5398
5941
|
}
|
|
5399
5942
|
},
|
|
5400
5943
|
"datasources count": {
|
|
5401
|
-
usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]",
|
|
5944
|
+
usage: "Usage: remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]",
|
|
5945
|
+
flags: {
|
|
5946
|
+
source: { type: "string" },
|
|
5947
|
+
filter: { type: "string" },
|
|
5948
|
+
entities: { type: "string" },
|
|
5949
|
+
contains: { type: "string" },
|
|
5950
|
+
phrase: { type: "string" },
|
|
5951
|
+
candidate: { type: "boolean" }
|
|
5952
|
+
}
|
|
5953
|
+
},
|
|
5954
|
+
"datasources entities": {
|
|
5955
|
+
usage: "Usage: remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--limit <n>] [--candidate]",
|
|
5956
|
+
flags: {
|
|
5957
|
+
source: { type: "string" },
|
|
5958
|
+
filter: { type: "string" },
|
|
5959
|
+
entities: { type: "string" },
|
|
5960
|
+
contains: { type: "string" },
|
|
5961
|
+
phrase: { type: "string" },
|
|
5962
|
+
limit: { type: "number", min: 1 },
|
|
5963
|
+
candidate: { type: "boolean" }
|
|
5964
|
+
}
|
|
5965
|
+
},
|
|
5966
|
+
"datasources passages": {
|
|
5967
|
+
usage: "Usage: remy-admin datasources passages [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--cursor <cursor>] [--limit <n>] [--candidate]",
|
|
5402
5968
|
flags: {
|
|
5403
5969
|
source: { type: "string" },
|
|
5404
5970
|
filter: { type: "string" },
|
|
5971
|
+
entities: { type: "string" },
|
|
5405
5972
|
contains: { type: "string" },
|
|
5406
5973
|
phrase: { type: "string" },
|
|
5974
|
+
cursor: { type: "string" },
|
|
5975
|
+
limit: { type: "number", min: 1 },
|
|
5407
5976
|
candidate: { type: "boolean" }
|
|
5408
5977
|
}
|
|
5409
5978
|
},
|
|
@@ -5449,22 +6018,24 @@ async function dataSourcesAdd(ctx, a) {
|
|
|
5449
6018
|
const files = a.rest("file");
|
|
5450
6019
|
const slug = sourceOf(a);
|
|
5451
6020
|
const metadata = parseKeyValuePairs(a.str("metadata"), "metadata");
|
|
6021
|
+
const entities2 = parseIdList(a.str("entities"));
|
|
5452
6022
|
const results = [];
|
|
5453
6023
|
for (const file of files) {
|
|
5454
|
-
const abs =
|
|
6024
|
+
const abs = path6.isAbsolute(file) ? file : path6.join(WORKSPACE_DIR, file);
|
|
5455
6025
|
let bytes;
|
|
5456
6026
|
try {
|
|
5457
|
-
bytes =
|
|
6027
|
+
bytes = fs6.readFileSync(abs);
|
|
5458
6028
|
} catch (err) {
|
|
5459
6029
|
fatal(`Could not read file "${file}": ${err.message}`);
|
|
5460
6030
|
}
|
|
5461
|
-
const filename =
|
|
6031
|
+
const filename = path6.basename(file);
|
|
5462
6032
|
results.push(
|
|
5463
6033
|
await addDocument(ctx, {
|
|
5464
6034
|
slug,
|
|
5465
6035
|
filename,
|
|
5466
6036
|
content: bytes,
|
|
5467
6037
|
...metadata ? { metadata } : {},
|
|
6038
|
+
...entities2 ? { entities: entities2 } : {},
|
|
5468
6039
|
onProgress: progress
|
|
5469
6040
|
})
|
|
5470
6041
|
);
|
|
@@ -5535,6 +6106,7 @@ async function dataSourcesStatus(ctx, a) {
|
|
|
5535
6106
|
activeVersion: source.activeVersion,
|
|
5536
6107
|
candidate: source.candidate,
|
|
5537
6108
|
job: source.job,
|
|
6109
|
+
relink: source.relink,
|
|
5538
6110
|
failures: (failures?.documents ?? []).map(summarize),
|
|
5539
6111
|
moreFailures: !!failures?.nextCursor
|
|
5540
6112
|
});
|
|
@@ -5550,16 +6122,7 @@ async function dataSourcesRm(ctx, a) {
|
|
|
5550
6122
|
out({ deleted: documentId });
|
|
5551
6123
|
return;
|
|
5552
6124
|
}
|
|
5553
|
-
|
|
5554
|
-
if (rawFilter.trimStart().startsWith("{")) {
|
|
5555
|
-
try {
|
|
5556
|
-
filter = JSON.parse(rawFilter);
|
|
5557
|
-
} catch (err) {
|
|
5558
|
-
fatal(`--filter is not valid JSON: ${err.message}`);
|
|
5559
|
-
}
|
|
5560
|
-
} else {
|
|
5561
|
-
filter = { metadata: parseKeyValuePairs(rawFilter, "filter") };
|
|
5562
|
-
}
|
|
6125
|
+
const filter = documentSelectorOf(rawFilter);
|
|
5563
6126
|
const slug = sourceOf(a);
|
|
5564
6127
|
const result = await rmWhereAll(ctx, {
|
|
5565
6128
|
slug,
|
|
@@ -5614,6 +6177,40 @@ async function dataSourcesCount(ctx, a) {
|
|
|
5614
6177
|
})
|
|
5615
6178
|
);
|
|
5616
6179
|
}
|
|
6180
|
+
async function dataSourcesEntities(ctx, a) {
|
|
6181
|
+
const filter = filterFromFlags(a);
|
|
6182
|
+
const limit = a.num("limit");
|
|
6183
|
+
out(
|
|
6184
|
+
await entities(ctx, {
|
|
6185
|
+
slug: sourceOf(a),
|
|
6186
|
+
...filter ? { filter } : {},
|
|
6187
|
+
...limit !== void 0 ? { limit } : {},
|
|
6188
|
+
...a.bool("candidate") ? { candidate: true } : {}
|
|
6189
|
+
})
|
|
6190
|
+
);
|
|
6191
|
+
}
|
|
6192
|
+
async function dataSourcesPassages(ctx, a) {
|
|
6193
|
+
const filter = filterFromFlags(a);
|
|
6194
|
+
const limit = a.num("limit");
|
|
6195
|
+
const cursor = a.str("cursor");
|
|
6196
|
+
const result = await passages(ctx, {
|
|
6197
|
+
slug: sourceOf(a),
|
|
6198
|
+
...filter ? { filter } : {},
|
|
6199
|
+
...cursor ? { cursor } : {},
|
|
6200
|
+
...limit !== void 0 ? { limit } : {},
|
|
6201
|
+
...a.bool("candidate") ? { candidate: true } : {}
|
|
6202
|
+
});
|
|
6203
|
+
out({
|
|
6204
|
+
...result,
|
|
6205
|
+
...result.nextCursor ? { note: `More: pass --cursor ${result.nextCursor} for the next page.` } : {}
|
|
6206
|
+
});
|
|
6207
|
+
}
|
|
6208
|
+
function parseIdList(raw) {
|
|
6209
|
+
if (raw === void 0) {
|
|
6210
|
+
return void 0;
|
|
6211
|
+
}
|
|
6212
|
+
return raw.split(",").map((id) => id.trim()).filter((id) => id.length > 0);
|
|
6213
|
+
}
|
|
5617
6214
|
function filterFromFlags(a) {
|
|
5618
6215
|
let filter = {};
|
|
5619
6216
|
const raw = a.str("filter");
|
|
@@ -5631,6 +6228,10 @@ function filterFromFlags(a) {
|
|
|
5631
6228
|
}
|
|
5632
6229
|
}
|
|
5633
6230
|
}
|
|
6231
|
+
const entities2 = parseIdList(a.str("entities"));
|
|
6232
|
+
if (entities2?.length) {
|
|
6233
|
+
filter.entities = entities2;
|
|
6234
|
+
}
|
|
5634
6235
|
const phrase = a.str("phrase");
|
|
5635
6236
|
if (phrase) {
|
|
5636
6237
|
filter.phrase = phrase;
|
|
@@ -5641,29 +6242,6 @@ function filterFromFlags(a) {
|
|
|
5641
6242
|
}
|
|
5642
6243
|
return Object.keys(filter).length ? filter : void 0;
|
|
5643
6244
|
}
|
|
5644
|
-
function parseKeyValuePairs(raw, flag) {
|
|
5645
|
-
if (raw === void 0) {
|
|
5646
|
-
return void 0;
|
|
5647
|
-
}
|
|
5648
|
-
const pairs2 = {};
|
|
5649
|
-
for (const part of raw.split(",")) {
|
|
5650
|
-
const trimmed = part.trim();
|
|
5651
|
-
if (!trimmed) {
|
|
5652
|
-
continue;
|
|
5653
|
-
}
|
|
5654
|
-
const eq = trimmed.indexOf("=");
|
|
5655
|
-
if (eq <= 0) {
|
|
5656
|
-
fatal(`--${flag} entries must be key=value (got "${trimmed}").`);
|
|
5657
|
-
}
|
|
5658
|
-
const key = trimmed.slice(0, eq).trim();
|
|
5659
|
-
const value = trimmed.slice(eq + 1).trim();
|
|
5660
|
-
pairs2[key] = value === "true" ? true : value === "false" ? false : value !== "" && !Number.isNaN(Number(value)) ? Number(value) : value;
|
|
5661
|
-
}
|
|
5662
|
-
if (Object.keys(pairs2).length === 0) {
|
|
5663
|
-
fatal(`--${flag} needs at least one key=value pair.`);
|
|
5664
|
-
}
|
|
5665
|
-
return pairs2;
|
|
5666
|
-
}
|
|
5667
6245
|
function triState2(a, flag, key) {
|
|
5668
6246
|
const raw = a.str(flag);
|
|
5669
6247
|
if (raw === void 0) {
|
|
@@ -5890,6 +6468,8 @@ var dataSourcesHandlers = {
|
|
|
5890
6468
|
"datasources create": dataSourcesCreate,
|
|
5891
6469
|
"datasources config": dataSourcesConfig,
|
|
5892
6470
|
"datasources count": dataSourcesCount,
|
|
6471
|
+
"datasources entities": dataSourcesEntities,
|
|
6472
|
+
"datasources passages": dataSourcesPassages,
|
|
5893
6473
|
"datasources move": dataSourcesMove,
|
|
5894
6474
|
"datasources revectorize": dataSourcesRevectorize,
|
|
5895
6475
|
"datasources promote": dataSourcesPromote,
|
|
@@ -5907,6 +6487,11 @@ Subcommands:
|
|
|
5907
6487
|
status Show a source's counts and its failed documents (--all walks every document)
|
|
5908
6488
|
rm Remove a document, or every document matching a filter
|
|
5909
6489
|
search Query a corpus \u2014 useful to sanity-check one you just built
|
|
6490
|
+
count How many passages match a filter (words, a phrase, entities, metadata)
|
|
6491
|
+
passages Every passage matching a filter, unranked, a page at a time
|
|
6492
|
+
entities Which entities the matching passages are about, most first
|
|
6493
|
+
dictionary push|status: send a source its dictionary of names, or show the current one
|
|
6494
|
+
link What the source's dictionary finds in a piece of text
|
|
5910
6495
|
create Create an empty source, optionally on dedicated capacity
|
|
5911
6496
|
config Show or change how a corpus is processed and searched
|
|
5912
6497
|
move Move a corpus between shared and dedicated capacity, data intact
|
|
@@ -5919,6 +6504,8 @@ Subcommands:
|
|
|
5919
6504
|
map test Run a source's mapper over real objects and show the outcomes; nothing ingested
|
|
5920
6505
|
map deploy Make a build's compiled mapper the source's active one (a branch build will do)
|
|
5921
6506
|
remap Re-apply the active mapper to every raw copy the source holds
|
|
6507
|
+
reader test|status|retry: run the dev session's reader, see its deliveries, resend failed ones
|
|
6508
|
+
reread Read a source's documents again with its live reader, priced first
|
|
5922
6509
|
hydrate Reload an evicted index ahead of the first search
|
|
5923
6510
|
reindex Recreate a dedicated source's collection with the current shape, refilled from stored vectors
|
|
5924
6511
|
sample Draw a sample of a source into a new one with the same config
|
|
@@ -5929,12 +6516,14 @@ Subcommands:
|
|
|
5929
6516
|
delete Delete a whole data source \u2014 documents, vectors and versions
|
|
5930
6517
|
|
|
5931
6518
|
Usage:
|
|
5932
|
-
remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>
|
|
6519
|
+
remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--entities <id,...>] [--wait] [--timeout <sec>] <file...>
|
|
5933
6520
|
remy-admin datasources list
|
|
5934
6521
|
remy-admin datasources status [--source <slug>] [--status error] [--all]
|
|
5935
6522
|
remy-admin datasources rm [--source <slug>] (--document <id> | --filter <k=v,...|json>)
|
|
5936
6523
|
remy-admin datasources search [--source <slug>] [search options] <query>
|
|
5937
|
-
remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--contains <words>] [--phrase <text>] [--candidate]
|
|
6524
|
+
remy-admin datasources count [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--candidate]
|
|
6525
|
+
remy-admin datasources entities [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--limit <n>] [--candidate]
|
|
6526
|
+
remy-admin datasources passages [--source <slug>] [--filter <k=v,...|json>] [--entities <id,...>] [--contains <words>] [--phrase <text>] [--cursor <c>] [--limit <n>] [--candidate]
|
|
5938
6527
|
remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
|
|
5939
6528
|
remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
|
|
5940
6529
|
remy-admin datasources move [--source <slug>] --to <resource-id|shared> [--chains <n>] [--restart] [--wait] [--timeout <sec>]
|
|
@@ -5955,6 +6544,15 @@ Count:
|
|
|
5955
6544
|
count (none exists), and not the set search returns. The SDK's
|
|
5956
6545
|
\`Source.count(filter)\` is the same call for an app.
|
|
5957
6546
|
|
|
6547
|
+
Entities:
|
|
6548
|
+
A document can assert what it is about as your own ids (\`add --entities\`,
|
|
6549
|
+
a mapper's \`entities\`, a manifest line's \`entities\`), and every passage
|
|
6550
|
+
carries them. \`--entities a,b\` on search and count narrows to passages
|
|
6551
|
+
about any of them. \`entities\` lists which entities the matching passages
|
|
6552
|
+
are about, most first, with how many passages carry each (approximate, and
|
|
6553
|
+
passages, not documents): filter on one entity to see what appears beside
|
|
6554
|
+
it. The SDK's \`Source.entities({ filter, limit })\` is the same call.
|
|
6555
|
+
|
|
5958
6556
|
Search options:
|
|
5959
6557
|
--top-k <n> Results to return (default 5, max 50)
|
|
5960
6558
|
--mode <m> hybrid (default) | semantic | lexical. Lexical is
|
|
@@ -5963,11 +6561,12 @@ Search options:
|
|
|
5963
6561
|
--filter <k=v,...> Match document metadata set at add time. Values
|
|
5964
6562
|
coerce: true/false and numbers become typed.
|
|
5965
6563
|
Pass a JSON object for the full grammar:
|
|
5966
|
-
metadata, filename, documentIds,
|
|
5967
|
-
phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
|
|
6564
|
+
metadata, filename, documentIds, entities, pages,
|
|
6565
|
+
contains, phrase \u2014 e.g. --filter '{"pages":{"max":3}}'.
|
|
5968
6566
|
Metadata takes numeric ranges via {gte,lte}: store
|
|
5969
6567
|
dates as integers (YYYYMMDD or epoch seconds), then
|
|
5970
6568
|
--filter '{"metadata":{"date":{"gte":20250101}}}'
|
|
6569
|
+
--entities <id,...> Chunk must be about any of these entities
|
|
5971
6570
|
--phrase <text> Chunk must contain this exact word sequence
|
|
5972
6571
|
--contains <words> Chunk must contain ALL these words, any order
|
|
5973
6572
|
--max-per-document <n> Cap hits per document; backfills from others
|
|
@@ -6056,6 +6655,8 @@ Notes:
|
|
|
6056
6655
|
Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
|
|
6057
6656
|
with a different --metadata updates the tags in place, also free.
|
|
6058
6657
|
--metadata tags documents for search-time filtering (scalars, \u226416 keys).
|
|
6658
|
+
--entities names what a document is about, as your own ids (\u2264256, no spaces
|
|
6659
|
+
or commas); an empty --entities clears them. Also free on a re-add.
|
|
6059
6660
|
rm --filter removes every matching document in pages of a thousand: k=v
|
|
6060
6661
|
pairs match metadata; a JSON object takes the full selector \u2014 metadata,
|
|
6061
6662
|
filename, documentIds, externalIdPrefix (the key a job or connector
|
|
@@ -6095,7 +6696,7 @@ Examples:
|
|
|
6095
6696
|
remy-admin datasources revectorize --source archive-sample --max-chars 900 --wait
|
|
6096
6697
|
remy-admin datasources eval run --set <setId> --candidate --label small-chunks --wait
|
|
6097
6698
|
remy-admin datasources eval compare <runA> <runB>
|
|
6098
|
-
${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceEvalsHelp}`;
|
|
6699
|
+
${dataSourceJobsHelp}${dataSourceConnectorsHelp}${dataSourceMappersHelp}${dataSourceReadersHelp}${dataSourceEvalsHelp}${dataSourceDictionaryHelp}`;
|
|
6099
6700
|
|
|
6100
6701
|
// src/ops/voice.ts
|
|
6101
6702
|
function normalizeE164(input) {
|
|
@@ -7932,21 +8533,21 @@ Examples:
|
|
|
7932
8533
|
// src/ops/infra.ts
|
|
7933
8534
|
var DEFAULT_WAIT_TIMEOUT_MS4 = 10 * 60 * 1e3;
|
|
7934
8535
|
var POLL_MS5 = 3e3;
|
|
7935
|
-
function
|
|
8536
|
+
function base7(appId) {
|
|
7936
8537
|
return `/_internal/v2/apps/${appId}/infra`;
|
|
7937
8538
|
}
|
|
7938
8539
|
function list14(ctx, params) {
|
|
7939
8540
|
return call(
|
|
7940
8541
|
ctx,
|
|
7941
8542
|
"GET",
|
|
7942
|
-
`${
|
|
8543
|
+
`${base7(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
|
|
7943
8544
|
);
|
|
7944
8545
|
}
|
|
7945
8546
|
function get12(ctx, params) {
|
|
7946
8547
|
return call(
|
|
7947
8548
|
ctx,
|
|
7948
8549
|
"GET",
|
|
7949
|
-
`${
|
|
8550
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}`
|
|
7950
8551
|
);
|
|
7951
8552
|
}
|
|
7952
8553
|
function logs(ctx, params) {
|
|
@@ -7961,11 +8562,11 @@ function logs(ctx, params) {
|
|
|
7961
8562
|
return call(
|
|
7962
8563
|
ctx,
|
|
7963
8564
|
"GET",
|
|
7964
|
-
`${
|
|
8565
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/logs${search2 ? `?${search2}` : ""}`
|
|
7965
8566
|
);
|
|
7966
8567
|
}
|
|
7967
8568
|
function provision(ctx, params) {
|
|
7968
|
-
return call(ctx, "POST",
|
|
8569
|
+
return call(ctx, "POST", base7(ctx.appId), {
|
|
7969
8570
|
offeringId: params.offeringId,
|
|
7970
8571
|
...params.name ? { name: params.name } : {}
|
|
7971
8572
|
});
|
|
@@ -7974,28 +8575,28 @@ function hibernate(ctx, params) {
|
|
|
7974
8575
|
return call(
|
|
7975
8576
|
ctx,
|
|
7976
8577
|
"POST",
|
|
7977
|
-
`${
|
|
8578
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
|
|
7978
8579
|
);
|
|
7979
8580
|
}
|
|
7980
8581
|
function resume2(ctx, params) {
|
|
7981
8582
|
return call(
|
|
7982
8583
|
ctx,
|
|
7983
8584
|
"POST",
|
|
7984
|
-
`${
|
|
8585
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
|
|
7985
8586
|
);
|
|
7986
8587
|
}
|
|
7987
8588
|
function destroy(ctx, params) {
|
|
7988
8589
|
return call(
|
|
7989
8590
|
ctx,
|
|
7990
8591
|
"POST",
|
|
7991
|
-
`${
|
|
8592
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
|
|
7992
8593
|
);
|
|
7993
8594
|
}
|
|
7994
8595
|
function resize(ctx, params) {
|
|
7995
8596
|
return call(
|
|
7996
8597
|
ctx,
|
|
7997
8598
|
"POST",
|
|
7998
|
-
`${
|
|
8599
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/resize`,
|
|
7999
8600
|
{ offeringId: params.offeringId }
|
|
8000
8601
|
);
|
|
8001
8602
|
}
|
|
@@ -8003,7 +8604,7 @@ function rename(ctx, params) {
|
|
|
8003
8604
|
return call(
|
|
8004
8605
|
ctx,
|
|
8005
8606
|
"POST",
|
|
8006
|
-
`${
|
|
8607
|
+
`${base7(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
|
|
8007
8608
|
{ name: params.name }
|
|
8008
8609
|
);
|
|
8009
8610
|
}
|
|
@@ -8572,7 +9173,9 @@ var SPECS = {
|
|
|
8572
9173
|
...dataSourceJobsSpecs,
|
|
8573
9174
|
...dataSourceConnectorsSpecs,
|
|
8574
9175
|
...dataSourceEvalsSpecs,
|
|
9176
|
+
...dataSourceDictionarySpecs,
|
|
8575
9177
|
...dataSourceMappersSpecs,
|
|
9178
|
+
...dataSourceReadersSpecs,
|
|
8576
9179
|
...voiceSpecs,
|
|
8577
9180
|
...agentSpecs,
|
|
8578
9181
|
...tasksSpecs,
|
|
@@ -8603,7 +9206,9 @@ var HANDLERS = {
|
|
|
8603
9206
|
...dataSourceJobsHandlers,
|
|
8604
9207
|
...dataSourceConnectorsHandlers,
|
|
8605
9208
|
...dataSourceEvalsHandlers,
|
|
9209
|
+
...dataSourceDictionaryHandlers,
|
|
8606
9210
|
...dataSourceMappersHandlers,
|
|
9211
|
+
...dataSourceReadersHandlers,
|
|
8607
9212
|
...voiceHandlers,
|
|
8608
9213
|
...agentHandlers,
|
|
8609
9214
|
...tasksHandlers,
|
|
@@ -8644,8 +9249,8 @@ var GROUP_HELP = {
|
|
|
8644
9249
|
};
|
|
8645
9250
|
|
|
8646
9251
|
// src/ctx.ts
|
|
8647
|
-
import
|
|
8648
|
-
import
|
|
9252
|
+
import fs7 from "fs";
|
|
9253
|
+
import path7 from "path";
|
|
8649
9254
|
|
|
8650
9255
|
// src/parseJsonConfig.ts
|
|
8651
9256
|
import JSON5 from "json5";
|
|
@@ -8674,10 +9279,10 @@ function parseJsonConfig(raw) {
|
|
|
8674
9279
|
// src/ctx.ts
|
|
8675
9280
|
var DEFAULT_BASE_URL = "https://api.mindstudio.ai";
|
|
8676
9281
|
function loadWorkspaceAppId(workspaceDir = WORKSPACE_DIR) {
|
|
8677
|
-
const manifestPath =
|
|
9282
|
+
const manifestPath = path7.join(workspaceDir, "mindstudio.json");
|
|
8678
9283
|
let raw;
|
|
8679
9284
|
try {
|
|
8680
|
-
raw =
|
|
9285
|
+
raw = fs7.readFileSync(manifestPath, "utf-8");
|
|
8681
9286
|
} catch (err) {
|
|
8682
9287
|
if (err.code === "ENOENT") {
|
|
8683
9288
|
throw new Error(`mindstudio.json not found at ${manifestPath}`);
|
|
@@ -8775,32 +9380,60 @@ function resolveRoute(argv) {
|
|
|
8775
9380
|
if (sub && KEYS.has(two)) {
|
|
8776
9381
|
return { key: two, argv: argv.slice(2) };
|
|
8777
9382
|
}
|
|
8778
|
-
if (group === "
|
|
8779
|
-
return {
|
|
8780
|
-
error: `Unknown subcommand: domains custom ${action ?? ""}. Run 'remy-admin domains --help'`
|
|
8781
|
-
};
|
|
9383
|
+
if (group === "db" && sub) {
|
|
9384
|
+
return { key: "db query", argv: argv.slice(1) };
|
|
8782
9385
|
}
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
9386
|
+
return { error: unknownSubcommand(group, sub, action) };
|
|
9387
|
+
}
|
|
9388
|
+
function keysUnder(words) {
|
|
9389
|
+
const prefix = `${words.join(" ")} `;
|
|
9390
|
+
return [...KEYS].filter((key) => key.startsWith(prefix)).sort();
|
|
9391
|
+
}
|
|
9392
|
+
function unknownSubcommand(group, sub, action) {
|
|
9393
|
+
const asked = [group, sub, action].filter(Boolean).join(" ");
|
|
9394
|
+
const nested = sub ? keysUnder([group, sub]) : [];
|
|
9395
|
+
if (nested.length > 0) {
|
|
9396
|
+
const actions = nested.map((key) => key.split(" ")[2]);
|
|
9397
|
+
return `Unknown subcommand: ${asked}. '${group} ${sub}' takes: ${actions.join(", ")}. Run 'remy-admin ${group} ${sub} --help' for their usage.`;
|
|
8787
9398
|
}
|
|
8788
|
-
|
|
8789
|
-
|
|
8790
|
-
|
|
8791
|
-
|
|
9399
|
+
const subs = [...new Set(keysUnder([group]).map((key) => key.split(" ")[1]))];
|
|
9400
|
+
return `Unknown subcommand: ${asked}. '${group}' takes: ${subs.join(", ")}. Run 'remy-admin ${group} --help'.`;
|
|
9401
|
+
}
|
|
9402
|
+
function leadingWords(argv) {
|
|
9403
|
+
const words = [];
|
|
9404
|
+
for (const token of argv) {
|
|
9405
|
+
if (token.startsWith("-")) {
|
|
9406
|
+
break;
|
|
9407
|
+
}
|
|
9408
|
+
words.push(token);
|
|
8792
9409
|
}
|
|
8793
|
-
|
|
8794
|
-
|
|
8795
|
-
|
|
8796
|
-
|
|
9410
|
+
return words;
|
|
9411
|
+
}
|
|
9412
|
+
function commandHelp(argv) {
|
|
9413
|
+
const words = leadingWords(argv);
|
|
9414
|
+
const group = words[0];
|
|
9415
|
+
const guide = `Run 'remy-admin ${group} --help' for the ${group} guide.`;
|
|
9416
|
+
for (let n = Math.min(words.length, 3); n >= 2; n--) {
|
|
9417
|
+
const key = words.slice(0, n).join(" ");
|
|
9418
|
+
if (KEYS.has(key)) {
|
|
9419
|
+
return `${SPECS[key].usage}
|
|
9420
|
+
|
|
9421
|
+
${guide}`;
|
|
9422
|
+
}
|
|
8797
9423
|
}
|
|
8798
|
-
if (
|
|
8799
|
-
|
|
9424
|
+
if (words.length >= 2) {
|
|
9425
|
+
const under = keysUnder(words.slice(0, 2));
|
|
9426
|
+
if (under.length > 0) {
|
|
9427
|
+
const usages = under.map(
|
|
9428
|
+
(key) => ` ${SPECS[key].usage.replace(/^Usage:\s*/, "")}`
|
|
9429
|
+
);
|
|
9430
|
+
return `Usage:
|
|
9431
|
+
${usages.join("\n")}
|
|
9432
|
+
|
|
9433
|
+
${guide}`;
|
|
9434
|
+
}
|
|
8800
9435
|
}
|
|
8801
|
-
return
|
|
8802
|
-
error: `Unknown subcommand: ${group} ${sub ?? ""}. Run 'remy-admin ${group} --help'`
|
|
8803
|
-
};
|
|
9436
|
+
return void 0;
|
|
8804
9437
|
}
|
|
8805
9438
|
function resolveHelp(argv) {
|
|
8806
9439
|
const group = argv[0];
|
|
@@ -8815,7 +9448,10 @@ function resolveHelp(argv) {
|
|
|
8815
9448
|
return groupHelp;
|
|
8816
9449
|
}
|
|
8817
9450
|
const scan = group === "db" ? argv.slice(1, argv[1] === "query" ? 3 : 2) : argv.slice(1);
|
|
8818
|
-
|
|
9451
|
+
if (!scan.some((t) => t === "--help" || t === "-h")) {
|
|
9452
|
+
return void 0;
|
|
9453
|
+
}
|
|
9454
|
+
return commandHelp(argv) ?? groupHelp;
|
|
8819
9455
|
}
|
|
8820
9456
|
|
|
8821
9457
|
// src/prod.ts
|