@madewithremy/admin 0.1.5 → 0.1.6
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 +395 -24
- package/dist/index.js +212 -28
- package/dist/index.js.map +1 -1
- package/dist/prod.js +462 -31
- package/package.json +1 -1
package/dist/prod.js
CHANGED
|
@@ -228,6 +228,16 @@ function parseCommand(spec, argv) {
|
|
|
228
228
|
|
|
229
229
|
// src/http.ts
|
|
230
230
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
231
|
+
var TRANSIENT_GRACE_MS = 2 * 6e4;
|
|
232
|
+
function isTransientError(err) {
|
|
233
|
+
if (err instanceof AdminApiError) {
|
|
234
|
+
return err.status >= 500;
|
|
235
|
+
}
|
|
236
|
+
if (err instanceof AdminTimeoutError) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
return err instanceof TypeError;
|
|
240
|
+
}
|
|
231
241
|
var REPORT_TIMEOUT_MS = 6e4;
|
|
232
242
|
function seg(value) {
|
|
233
243
|
return encodeURIComponent(String(value));
|
|
@@ -913,7 +923,9 @@ function summarizeRelease(release) {
|
|
|
913
923
|
}
|
|
914
924
|
return {
|
|
915
925
|
...summary2,
|
|
916
|
-
error: buildFailureReason(release) ?? "Build failed (no error entry in the build log)"
|
|
926
|
+
error: release.failureReason ?? buildFailureReason(release) ?? "Build failed (no error entry in the build log)",
|
|
927
|
+
...release.failureKind ? { failureKind: release.failureKind } : {},
|
|
928
|
+
...release.failurePhase ? { failurePhase: release.failurePhase } : {}
|
|
917
929
|
};
|
|
918
930
|
}
|
|
919
931
|
var POLL_MS = 3e3;
|
|
@@ -1164,6 +1176,12 @@ the exit code so you can branch on $? without parsing:
|
|
|
1164
1176
|
Any other failure (bad arguments, auth, API error) exits 10, so exit 1 always
|
|
1165
1177
|
means the build itself failed.
|
|
1166
1178
|
|
|
1179
|
+
Every release shape ('get', 'list', 'status', 'wait') carries why a failed
|
|
1180
|
+
build failed: 'failureReason' (message), 'failurePhase' (the stage that was
|
|
1181
|
+
running), and 'failureKind' \u2014 'build' (the code), 'unbuildable' (commit is
|
|
1182
|
+
not a readable app), or 'interrupted' (a platform restart killed the build;
|
|
1183
|
+
a retry is already running, so don't re-push).
|
|
1184
|
+
|
|
1167
1185
|
Examples:
|
|
1168
1186
|
git push origin HEAD && remy-admin releases wait
|
|
1169
1187
|
remy-admin releases wait --commit 91ca67a --timeout 600
|
|
@@ -2919,12 +2937,26 @@ async function waitForIngest(ctx, params) {
|
|
|
2919
2937
|
}
|
|
2920
2938
|
const wanted = new Set(documentIds);
|
|
2921
2939
|
const start = Date.now();
|
|
2940
|
+
let transientSince = null;
|
|
2922
2941
|
for (; ; ) {
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
)
|
|
2942
|
+
let docs;
|
|
2943
|
+
try {
|
|
2944
|
+
docs = await allDocuments(ctx, { slug, ids: documentIds });
|
|
2945
|
+
transientSince = null;
|
|
2946
|
+
} catch (err) {
|
|
2947
|
+
if (!isTransientError(err)) {
|
|
2948
|
+
throw err;
|
|
2949
|
+
}
|
|
2950
|
+
transientSince ??= Date.now();
|
|
2951
|
+
if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
|
|
2952
|
+
throw err;
|
|
2953
|
+
}
|
|
2954
|
+
onProgress?.(
|
|
2955
|
+
`retrying after a gateway error\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
|
|
2956
|
+
);
|
|
2957
|
+
await sleep(POLL_MS2);
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2928
2960
|
const tracked = (docs ?? []).filter((d) => wanted.has(d.id));
|
|
2929
2961
|
const pending = tracked.filter((d) => d.status === "processing");
|
|
2930
2962
|
const failed = tracked.filter((d) => d.status === "error");
|
|
@@ -2949,12 +2981,41 @@ async function waitForIngest(ctx, params) {
|
|
|
2949
2981
|
await sleep(POLL_MS2);
|
|
2950
2982
|
}
|
|
2951
2983
|
}
|
|
2984
|
+
var DOCUMENTS_IDS_MAX = 200;
|
|
2952
2985
|
function documents(ctx, params) {
|
|
2953
2986
|
return call(
|
|
2954
2987
|
ctx,
|
|
2955
2988
|
"GET",
|
|
2956
|
-
`${base(ctx.appId)}/documents${qs({
|
|
2957
|
-
|
|
2989
|
+
`${base(ctx.appId)}/documents${qs({
|
|
2990
|
+
slug: params.slug,
|
|
2991
|
+
candidate: params.candidate || void 0,
|
|
2992
|
+
ids: params.ids?.length ? params.ids.join(",") : void 0,
|
|
2993
|
+
limit: params.limit,
|
|
2994
|
+
cursor: params.cursor
|
|
2995
|
+
})}`
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2998
|
+
async function allDocuments(ctx, params) {
|
|
2999
|
+
const groups = params.ids ? Array.from(
|
|
3000
|
+
{ length: Math.ceil(params.ids.length / DOCUMENTS_IDS_MAX) },
|
|
3001
|
+
(_, i) => params.ids.slice(i * DOCUMENTS_IDS_MAX, (i + 1) * DOCUMENTS_IDS_MAX)
|
|
3002
|
+
) : [void 0];
|
|
3003
|
+
const out2 = [];
|
|
3004
|
+
for (const ids of groups) {
|
|
3005
|
+
let cursor;
|
|
3006
|
+
do {
|
|
3007
|
+
const page = await documents(ctx, {
|
|
3008
|
+
slug: params.slug,
|
|
3009
|
+
candidate: params.candidate,
|
|
3010
|
+
ids,
|
|
3011
|
+
limit: 1e3,
|
|
3012
|
+
cursor
|
|
3013
|
+
});
|
|
3014
|
+
out2.push(...page.documents ?? []);
|
|
3015
|
+
cursor = page.nextCursor ?? void 0;
|
|
3016
|
+
} while (cursor);
|
|
3017
|
+
}
|
|
3018
|
+
return out2;
|
|
2958
3019
|
}
|
|
2959
3020
|
function list8(ctx) {
|
|
2960
3021
|
return call(ctx, "GET", base(ctx.appId));
|
|
@@ -2996,7 +3057,7 @@ function configGet(ctx, slug) {
|
|
|
2996
3057
|
);
|
|
2997
3058
|
}
|
|
2998
3059
|
function configSet(ctx, params) {
|
|
2999
|
-
const { slug, ingest, retrieval } = params;
|
|
3060
|
+
const { slug, ingest, retrieval, placement } = params;
|
|
3000
3061
|
return call(
|
|
3001
3062
|
ctx,
|
|
3002
3063
|
"POST",
|
|
@@ -3004,10 +3065,20 @@ function configSet(ctx, params) {
|
|
|
3004
3065
|
{
|
|
3005
3066
|
slug,
|
|
3006
3067
|
...ingest ? { ingest } : {},
|
|
3007
|
-
...retrieval ? { retrieval } : {}
|
|
3068
|
+
...retrieval ? { retrieval } : {},
|
|
3069
|
+
...placement !== void 0 ? { placement } : {}
|
|
3008
3070
|
}
|
|
3009
3071
|
);
|
|
3010
3072
|
}
|
|
3073
|
+
function create2(ctx, params) {
|
|
3074
|
+
const { slug, name, ingest, placement } = params;
|
|
3075
|
+
return call(ctx, "POST", base(ctx.appId), {
|
|
3076
|
+
slug,
|
|
3077
|
+
...name ? { name } : {},
|
|
3078
|
+
...ingest ? { ingest } : {},
|
|
3079
|
+
...placement !== void 0 ? { placement } : {}
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3011
3082
|
function revectorize(ctx, params) {
|
|
3012
3083
|
const { slug, ingest } = params;
|
|
3013
3084
|
return call(
|
|
@@ -3065,8 +3136,8 @@ function deleteSource(ctx, slug) {
|
|
|
3065
3136
|
// src/commands/dataSources.ts
|
|
3066
3137
|
var DEFAULT_SOURCE = "default";
|
|
3067
3138
|
var POLL_MS3 = 3e3;
|
|
3068
|
-
var
|
|
3069
|
-
|
|
3139
|
+
var INGEST_FLAGS = {
|
|
3140
|
+
chunking: { type: "string" },
|
|
3070
3141
|
"max-chars": { type: "number", min: 200 },
|
|
3071
3142
|
"min-chars": { type: "number", min: 0 },
|
|
3072
3143
|
"drop-blocks": { type: "string" },
|
|
@@ -3074,9 +3145,12 @@ var CONFIG_FLAGS = {
|
|
|
3074
3145
|
"contextual-model": { type: "string" },
|
|
3075
3146
|
"describe-images": { type: "string" },
|
|
3076
3147
|
"embedding-model": { type: "string" },
|
|
3148
|
+
"embedding-dimensions": { type: "number", min: 1 },
|
|
3077
3149
|
"image-model": { type: "string" },
|
|
3078
|
-
"extraction-model": { type: "string" }
|
|
3079
|
-
|
|
3150
|
+
"extraction-model": { type: "string" }
|
|
3151
|
+
};
|
|
3152
|
+
var CHUNKING_STRATEGIES = ["structural", "whole"];
|
|
3153
|
+
var RETRIEVAL_FLAGS = {
|
|
3080
3154
|
rerank: { type: "string" },
|
|
3081
3155
|
// Which cross-encoder reranks. Live, not pinned: a reranker runs at query
|
|
3082
3156
|
// time over text already in the index, so switching costs nothing and is
|
|
@@ -3085,6 +3159,10 @@ var CONFIG_FLAGS = {
|
|
|
3085
3159
|
hybrid: { type: "string" },
|
|
3086
3160
|
"top-k": { type: "number", min: 1 }
|
|
3087
3161
|
};
|
|
3162
|
+
var CONFIG_FLAGS = {
|
|
3163
|
+
...INGEST_FLAGS,
|
|
3164
|
+
...RETRIEVAL_FLAGS
|
|
3165
|
+
};
|
|
3088
3166
|
var dataSourcesSpecs = {
|
|
3089
3167
|
"datasources add": {
|
|
3090
3168
|
usage: "Usage: remy-admin datasources add [--source <slug>] [--metadata <k=v,...>] [--wait] [--timeout <sec>] <file...>",
|
|
@@ -3124,9 +3202,24 @@ var dataSourcesSpecs = {
|
|
|
3124
3202
|
highlight: { type: "boolean" }
|
|
3125
3203
|
}
|
|
3126
3204
|
},
|
|
3205
|
+
"datasources create": {
|
|
3206
|
+
usage: "Usage: remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [ingest settings...]",
|
|
3207
|
+
flags: {
|
|
3208
|
+
source: { type: "string" },
|
|
3209
|
+
name: { type: "string" },
|
|
3210
|
+
placement: { type: "string" },
|
|
3211
|
+
...INGEST_FLAGS
|
|
3212
|
+
}
|
|
3213
|
+
},
|
|
3127
3214
|
"datasources config": {
|
|
3128
|
-
usage: "Usage: remy-admin datasources config [--source <slug>] [settings...]",
|
|
3129
|
-
flags: {
|
|
3215
|
+
usage: "Usage: remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]",
|
|
3216
|
+
flags: {
|
|
3217
|
+
source: { type: "string" },
|
|
3218
|
+
// Where the corpus lives: a dedicated resource id from `infra list`, or
|
|
3219
|
+
// `shared`. Only changeable while the source has nothing built.
|
|
3220
|
+
placement: { type: "string" },
|
|
3221
|
+
...CONFIG_FLAGS
|
|
3222
|
+
}
|
|
3130
3223
|
},
|
|
3131
3224
|
"datasources revectorize": {
|
|
3132
3225
|
usage: "Usage: remy-admin datasources revectorize [--source <slug>] [settings...] [--wait]",
|
|
@@ -3215,10 +3308,10 @@ async function dataSourcesList(ctx) {
|
|
|
3215
3308
|
}
|
|
3216
3309
|
async function dataSourcesStatus(ctx, a) {
|
|
3217
3310
|
const slug = sourceOf(a);
|
|
3218
|
-
const
|
|
3311
|
+
const documents2 = await allDocuments(ctx, { slug });
|
|
3219
3312
|
out({
|
|
3220
3313
|
dataSource: slug,
|
|
3221
|
-
documents:
|
|
3314
|
+
documents: documents2.map(summarize)
|
|
3222
3315
|
});
|
|
3223
3316
|
}
|
|
3224
3317
|
async function dataSourcesRm(ctx, a) {
|
|
@@ -3327,6 +3420,15 @@ function triState(a, flag, key) {
|
|
|
3327
3420
|
}
|
|
3328
3421
|
function configFromFlags(a) {
|
|
3329
3422
|
const chunking = {};
|
|
3423
|
+
const strategy = a.str("chunking");
|
|
3424
|
+
if (strategy !== void 0) {
|
|
3425
|
+
if (!CHUNKING_STRATEGIES.includes(strategy)) {
|
|
3426
|
+
fatal(
|
|
3427
|
+
`--chunking must be one of: ${CHUNKING_STRATEGIES.join(", ")} (got "${strategy}").`
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
chunking.strategy = strategy;
|
|
3431
|
+
}
|
|
3330
3432
|
if (a.num("max-chars") !== void 0) {
|
|
3331
3433
|
chunking.maxChars = a.num("max-chars");
|
|
3332
3434
|
}
|
|
@@ -3346,6 +3448,7 @@ function configFromFlags(a) {
|
|
|
3346
3448
|
...a.str("image-model") ? { modelId: a.str("image-model") } : {}
|
|
3347
3449
|
};
|
|
3348
3450
|
const embeddingModel = a.str("embedding-model");
|
|
3451
|
+
const embeddingDimensions = a.num("embedding-dimensions");
|
|
3349
3452
|
const extractionModel = a.str("extraction-model");
|
|
3350
3453
|
const ingest = {};
|
|
3351
3454
|
if (Object.keys(chunking).length) {
|
|
@@ -3357,8 +3460,11 @@ function configFromFlags(a) {
|
|
|
3357
3460
|
if (Object.keys(images).length) {
|
|
3358
3461
|
ingest.images = images;
|
|
3359
3462
|
}
|
|
3360
|
-
if (embeddingModel) {
|
|
3361
|
-
ingest.embedding = {
|
|
3463
|
+
if (embeddingModel || embeddingDimensions !== void 0) {
|
|
3464
|
+
ingest.embedding = {
|
|
3465
|
+
...embeddingModel ? { modelId: embeddingModel } : {},
|
|
3466
|
+
...embeddingDimensions !== void 0 ? { dimensions: embeddingDimensions } : {}
|
|
3467
|
+
};
|
|
3362
3468
|
}
|
|
3363
3469
|
if (extractionModel) {
|
|
3364
3470
|
ingest.extraction = { modelId: extractionModel };
|
|
@@ -3381,10 +3487,32 @@ function configFromFlags(a) {
|
|
|
3381
3487
|
retrieval: Object.keys(retrieval).length ? retrieval : void 0
|
|
3382
3488
|
};
|
|
3383
3489
|
}
|
|
3490
|
+
var placementOf = (a) => {
|
|
3491
|
+
const flag = a.str("placement");
|
|
3492
|
+
return flag === void 0 ? void 0 : flag === "shared" ? "shared" : { resourceId: flag };
|
|
3493
|
+
};
|
|
3494
|
+
async function dataSourcesCreate(ctx, a) {
|
|
3495
|
+
const slug = a.str("source");
|
|
3496
|
+
if (!slug) {
|
|
3497
|
+
fatal("--source is required.");
|
|
3498
|
+
}
|
|
3499
|
+
const { ingest } = configFromFlags(a);
|
|
3500
|
+
const name = a.str("name");
|
|
3501
|
+
const placement = placementOf(a);
|
|
3502
|
+
out(
|
|
3503
|
+
await create2(ctx, {
|
|
3504
|
+
slug,
|
|
3505
|
+
...name ? { name } : {},
|
|
3506
|
+
...ingest ? { ingest } : {},
|
|
3507
|
+
...placement !== void 0 ? { placement } : {}
|
|
3508
|
+
})
|
|
3509
|
+
);
|
|
3510
|
+
}
|
|
3384
3511
|
async function dataSourcesConfig(ctx, a) {
|
|
3385
3512
|
const slug = sourceOf(a);
|
|
3386
3513
|
const { ingest, retrieval } = configFromFlags(a);
|
|
3387
|
-
|
|
3514
|
+
const placement = placementOf(a);
|
|
3515
|
+
if (!ingest && !retrieval && placement === void 0) {
|
|
3388
3516
|
out(await configGet(ctx, slug));
|
|
3389
3517
|
return;
|
|
3390
3518
|
}
|
|
@@ -3392,7 +3520,8 @@ async function dataSourcesConfig(ctx, a) {
|
|
|
3392
3520
|
await configSet(ctx, {
|
|
3393
3521
|
slug,
|
|
3394
3522
|
...ingest ? { ingest } : {},
|
|
3395
|
-
...retrieval ? { retrieval } : {}
|
|
3523
|
+
...retrieval ? { retrieval } : {},
|
|
3524
|
+
...placement !== void 0 ? { placement } : {}
|
|
3396
3525
|
})
|
|
3397
3526
|
);
|
|
3398
3527
|
}
|
|
@@ -3411,12 +3540,12 @@ async function dataSourcesRevectorize(ctx, a) {
|
|
|
3411
3540
|
const timeoutMs = timeoutSec ? timeoutSec * 1e3 : DEFAULT_WAIT_TIMEOUT_MS;
|
|
3412
3541
|
const start = Date.now();
|
|
3413
3542
|
for (; ; ) {
|
|
3414
|
-
const
|
|
3543
|
+
const documents2 = await allDocuments(ctx, {
|
|
3415
3544
|
slug,
|
|
3416
3545
|
candidate: true
|
|
3417
3546
|
});
|
|
3418
|
-
const pending =
|
|
3419
|
-
const failed =
|
|
3547
|
+
const pending = documents2.filter((d) => d.status === "processing");
|
|
3548
|
+
const failed = documents2.filter((d) => d.status === "error");
|
|
3420
3549
|
if (pending.length === 0) {
|
|
3421
3550
|
out({
|
|
3422
3551
|
dataSource: slug,
|
|
@@ -3469,6 +3598,7 @@ var dataSourcesHandlers = {
|
|
|
3469
3598
|
"datasources status": dataSourcesStatus,
|
|
3470
3599
|
"datasources rm": dataSourcesRm,
|
|
3471
3600
|
"datasources search": dataSourcesSearch,
|
|
3601
|
+
"datasources create": dataSourcesCreate,
|
|
3472
3602
|
"datasources config": dataSourcesConfig,
|
|
3473
3603
|
"datasources revectorize": dataSourcesRevectorize,
|
|
3474
3604
|
"datasources promote": dataSourcesPromote,
|
|
@@ -3486,6 +3616,7 @@ Subcommands:
|
|
|
3486
3616
|
status Show per-document ingest state
|
|
3487
3617
|
rm Remove a document and its vectors
|
|
3488
3618
|
search Query a corpus \u2014 useful to sanity-check one you just built
|
|
3619
|
+
create Create an empty source, optionally on dedicated capacity
|
|
3489
3620
|
config Show or change how a corpus is processed and searched
|
|
3490
3621
|
revectorize Rebuild a corpus under new settings, alongside the live one
|
|
3491
3622
|
promote Make a rebuilt version live
|
|
@@ -3498,7 +3629,8 @@ Usage:
|
|
|
3498
3629
|
remy-admin datasources status [--source <slug>]
|
|
3499
3630
|
remy-admin datasources rm [--source <slug>] --document <id>
|
|
3500
3631
|
remy-admin datasources search [--source <slug>] [search options] <query>
|
|
3501
|
-
remy-admin datasources
|
|
3632
|
+
remy-admin datasources create --source <slug> [--name <name>] [--placement <resource-id|shared>] [rebuild settings...]
|
|
3633
|
+
remy-admin datasources config [--source <slug>] [--placement <resource-id|shared>] [settings...]
|
|
3502
3634
|
remy-admin datasources revectorize [--source <slug>] [settings...] [--wait]
|
|
3503
3635
|
remy-admin datasources promote [--source <slug>] [--force]
|
|
3504
3636
|
remy-admin datasources drop [--source <slug>] [--version <n>]
|
|
@@ -3540,6 +3672,13 @@ Tuning a corpus:
|
|
|
3540
3672
|
reprocessed. Changing these on a corpus that already has documents is
|
|
3541
3673
|
REJECTED; use \`revectorize\` instead, which builds a new version alongside
|
|
3542
3674
|
the live one so search never degrades:
|
|
3675
|
+
--chunking <structural|whole>
|
|
3676
|
+
structural (default) splits on headings, blocks
|
|
3677
|
+
and paragraphs. whole embeds each document as one
|
|
3678
|
+
chunk: for corpora of short records (articles,
|
|
3679
|
+
tickets, product rows) where the document is the
|
|
3680
|
+
unit you want back. Documents over --max-chars
|
|
3681
|
+
(default 24000 for whole) fall back to structural.
|
|
3543
3682
|
--max-chars <n> Target chunk size (default 2000)
|
|
3544
3683
|
--min-chars <n> Merge chunks smaller than this (default 120)
|
|
3545
3684
|
--drop-blocks <a,b> Block types to discard, e.g. footer,header
|
|
@@ -3555,10 +3694,18 @@ Tuning a corpus:
|
|
|
3555
3694
|
invisible to search rather than merely ranked low.
|
|
3556
3695
|
--image-model <id>
|
|
3557
3696
|
--embedding-model <id>
|
|
3697
|
+
--embedding-dimensions <n> Vector width, for models that offer several
|
|
3698
|
+
(Matryoshka). Must be the model's native size or
|
|
3699
|
+
one it lists; the error names the choices.
|
|
3700
|
+
Smaller vectors cut index memory, and dedicated
|
|
3701
|
+
capacity needs, roughly in proportion.
|
|
3558
3702
|
--extraction-model <id>
|
|
3559
3703
|
|
|
3560
3704
|
Notes:
|
|
3561
|
-
--source defaults to "${DEFAULT_SOURCE}" and is created on first use
|
|
3705
|
+
--source defaults to "${DEFAULT_SOURCE}" and is created on first use, on the
|
|
3706
|
+
shared pool. A corpus meant for dedicated capacity (\`infra provision\`)
|
|
3707
|
+
starts with \`create --placement <resource-id>\`, since a source with built
|
|
3708
|
+
documents cannot move; \`config --placement\` moves one that is still empty.
|
|
3562
3709
|
--wait blocks until processing finishes, so you can search immediately after.
|
|
3563
3710
|
Exits ${EXIT.buildFailed} if a document failed, ${EXIT.timeout} on timeout.
|
|
3564
3711
|
Re-adding an unchanged file is free: no upload, no re-embedding. Re-adding
|
|
@@ -5333,6 +5480,286 @@ Examples:
|
|
|
5333
5480
|
remy-admin cron list --runs 50
|
|
5334
5481
|
remy-admin cron run dailyDigest`;
|
|
5335
5482
|
|
|
5483
|
+
// src/ops/infra.ts
|
|
5484
|
+
var DEFAULT_WAIT_TIMEOUT_MS2 = 10 * 60 * 1e3;
|
|
5485
|
+
var POLL_MS4 = 3e3;
|
|
5486
|
+
function base2(appId) {
|
|
5487
|
+
return `/_internal/v2/apps/${appId}/infra`;
|
|
5488
|
+
}
|
|
5489
|
+
function list11(ctx, params) {
|
|
5490
|
+
return call(
|
|
5491
|
+
ctx,
|
|
5492
|
+
"GET",
|
|
5493
|
+
`${base2(ctx.appId)}${params?.includeDestroyed ? "?includeDestroyed=true" : ""}`
|
|
5494
|
+
);
|
|
5495
|
+
}
|
|
5496
|
+
function get8(ctx, params) {
|
|
5497
|
+
return call(
|
|
5498
|
+
ctx,
|
|
5499
|
+
"GET",
|
|
5500
|
+
`${base2(ctx.appId)}/${encodeURIComponent(params.id)}`
|
|
5501
|
+
);
|
|
5502
|
+
}
|
|
5503
|
+
function provision(ctx, params) {
|
|
5504
|
+
return call(ctx, "POST", base2(ctx.appId), {
|
|
5505
|
+
offeringId: params.offeringId,
|
|
5506
|
+
...params.name ? { name: params.name } : {}
|
|
5507
|
+
});
|
|
5508
|
+
}
|
|
5509
|
+
function hibernate(ctx, params) {
|
|
5510
|
+
return call(
|
|
5511
|
+
ctx,
|
|
5512
|
+
"POST",
|
|
5513
|
+
`${base2(ctx.appId)}/${encodeURIComponent(params.id)}/hibernate`
|
|
5514
|
+
);
|
|
5515
|
+
}
|
|
5516
|
+
function resume(ctx, params) {
|
|
5517
|
+
return call(
|
|
5518
|
+
ctx,
|
|
5519
|
+
"POST",
|
|
5520
|
+
`${base2(ctx.appId)}/${encodeURIComponent(params.id)}/resume`
|
|
5521
|
+
);
|
|
5522
|
+
}
|
|
5523
|
+
function destroy(ctx, params) {
|
|
5524
|
+
return call(
|
|
5525
|
+
ctx,
|
|
5526
|
+
"POST",
|
|
5527
|
+
`${base2(ctx.appId)}/${encodeURIComponent(params.id)}/destroy`
|
|
5528
|
+
);
|
|
5529
|
+
}
|
|
5530
|
+
function rename(ctx, params) {
|
|
5531
|
+
return call(
|
|
5532
|
+
ctx,
|
|
5533
|
+
"POST",
|
|
5534
|
+
`${base2(ctx.appId)}/${encodeURIComponent(params.id)}/rename`,
|
|
5535
|
+
{ name: params.name }
|
|
5536
|
+
);
|
|
5537
|
+
}
|
|
5538
|
+
async function waitForPhase(ctx, params) {
|
|
5539
|
+
const timeoutMs = params.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS2;
|
|
5540
|
+
const wanted = new Set(params.phases);
|
|
5541
|
+
const start = Date.now();
|
|
5542
|
+
let transientSince = null;
|
|
5543
|
+
for (; ; ) {
|
|
5544
|
+
let resource;
|
|
5545
|
+
try {
|
|
5546
|
+
({ resource } = await get8(ctx, { id: params.id }));
|
|
5547
|
+
transientSince = null;
|
|
5548
|
+
} catch (err) {
|
|
5549
|
+
if (!isTransientError(err)) {
|
|
5550
|
+
throw err;
|
|
5551
|
+
}
|
|
5552
|
+
transientSince ??= Date.now();
|
|
5553
|
+
if (Date.now() - transientSince > TRANSIENT_GRACE_MS) {
|
|
5554
|
+
throw err;
|
|
5555
|
+
}
|
|
5556
|
+
params.onProgress?.(
|
|
5557
|
+
`retrying after a gateway error\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
|
|
5558
|
+
);
|
|
5559
|
+
await sleep(POLL_MS4);
|
|
5560
|
+
continue;
|
|
5561
|
+
}
|
|
5562
|
+
if (wanted.has(resource.phase)) {
|
|
5563
|
+
return { status: "done", resource };
|
|
5564
|
+
}
|
|
5565
|
+
if (resource.phase === "failed") {
|
|
5566
|
+
return {
|
|
5567
|
+
status: "failed",
|
|
5568
|
+
resource,
|
|
5569
|
+
error: resource.lastError ?? "The platform could not complete the change."
|
|
5570
|
+
};
|
|
5571
|
+
}
|
|
5572
|
+
if (Date.now() - start > timeoutMs) {
|
|
5573
|
+
return {
|
|
5574
|
+
status: "timeout",
|
|
5575
|
+
resource,
|
|
5576
|
+
error: `Timed out after ${Math.round(timeoutMs / 1e3)}s; resource is still ${resource.phase}`
|
|
5577
|
+
};
|
|
5578
|
+
}
|
|
5579
|
+
params.onProgress?.(
|
|
5580
|
+
`${resource.phase}${resource.detail ? ` \xB7 ${resource.detail}` : ""}\u2026 (${Math.round((Date.now() - start) / 1e3)}s)`
|
|
5581
|
+
);
|
|
5582
|
+
await sleep(POLL_MS4);
|
|
5583
|
+
}
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
// src/commands/infra.ts
|
|
5587
|
+
var infraSpecs = {
|
|
5588
|
+
"infra list": {
|
|
5589
|
+
usage: "Usage: remy-admin infra list [--include-destroyed]",
|
|
5590
|
+
flags: { "include-destroyed": { type: "boolean" } }
|
|
5591
|
+
},
|
|
5592
|
+
"infra get": {
|
|
5593
|
+
usage: "Usage: remy-admin infra get <id>",
|
|
5594
|
+
positionals: [{ name: "id", required: true }]
|
|
5595
|
+
},
|
|
5596
|
+
"infra provision": {
|
|
5597
|
+
usage: "Usage: remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]",
|
|
5598
|
+
flags: {
|
|
5599
|
+
offering: { type: "string" },
|
|
5600
|
+
name: { type: "string" },
|
|
5601
|
+
wait: { type: "boolean" },
|
|
5602
|
+
timeout: { type: "string" }
|
|
5603
|
+
},
|
|
5604
|
+
requireAnyOf: {
|
|
5605
|
+
flags: ["offering"],
|
|
5606
|
+
message: "--offering is required (see `infra list` for the catalog)."
|
|
5607
|
+
}
|
|
5608
|
+
},
|
|
5609
|
+
"infra hibernate": {
|
|
5610
|
+
usage: "Usage: remy-admin infra hibernate <id> [--wait] [--timeout <sec>]",
|
|
5611
|
+
positionals: [{ name: "id", required: true }],
|
|
5612
|
+
flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
|
|
5613
|
+
},
|
|
5614
|
+
"infra resume": {
|
|
5615
|
+
usage: "Usage: remy-admin infra resume <id> [--wait] [--timeout <sec>]",
|
|
5616
|
+
positionals: [{ name: "id", required: true }],
|
|
5617
|
+
flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
|
|
5618
|
+
},
|
|
5619
|
+
"infra destroy": {
|
|
5620
|
+
usage: "Usage: remy-admin infra destroy <id> [--wait] [--timeout <sec>]",
|
|
5621
|
+
positionals: [{ name: "id", required: true }],
|
|
5622
|
+
flags: { wait: { type: "boolean" }, timeout: { type: "string" } }
|
|
5623
|
+
},
|
|
5624
|
+
"infra rename": {
|
|
5625
|
+
usage: "Usage: remy-admin infra rename <id> --name <name>",
|
|
5626
|
+
positionals: [{ name: "id", required: true }],
|
|
5627
|
+
flags: { name: { type: "string" } },
|
|
5628
|
+
requireAnyOf: { flags: ["name"], message: "--name is required." }
|
|
5629
|
+
}
|
|
5630
|
+
};
|
|
5631
|
+
async function waitAndReport(ctx, a, id, phases, summary2) {
|
|
5632
|
+
const timeoutSec = a.str("timeout");
|
|
5633
|
+
const result = await waitForPhase(ctx, {
|
|
5634
|
+
id,
|
|
5635
|
+
phases,
|
|
5636
|
+
...timeoutSec ? { timeoutMs: Number(timeoutSec) * 1e3 } : {},
|
|
5637
|
+
onProgress: progress
|
|
5638
|
+
});
|
|
5639
|
+
out({ ...summary2, ...result });
|
|
5640
|
+
if (result.status === "failed") {
|
|
5641
|
+
throw new CliError(result.error, EXIT.buildFailed);
|
|
5642
|
+
}
|
|
5643
|
+
if (result.status === "timeout") {
|
|
5644
|
+
throw new CliError(result.error, EXIT.timeout);
|
|
5645
|
+
}
|
|
5646
|
+
}
|
|
5647
|
+
async function infraList(ctx, a) {
|
|
5648
|
+
out(await list11(ctx, { includeDestroyed: a.bool("include-destroyed") }));
|
|
5649
|
+
}
|
|
5650
|
+
async function infraGet(ctx, a) {
|
|
5651
|
+
out(await get8(ctx, { id: a.req("id") }));
|
|
5652
|
+
}
|
|
5653
|
+
async function infraProvision(ctx, a) {
|
|
5654
|
+
const offeringId = a.str("offering");
|
|
5655
|
+
const name = a.str("name");
|
|
5656
|
+
const { resource } = await provision(ctx, {
|
|
5657
|
+
offeringId,
|
|
5658
|
+
...name ? { name } : {}
|
|
5659
|
+
});
|
|
5660
|
+
if (!a.bool("wait")) {
|
|
5661
|
+
out({
|
|
5662
|
+
resource,
|
|
5663
|
+
note: "Provisioning; poll `infra get <id>` or re-run with --wait."
|
|
5664
|
+
});
|
|
5665
|
+
return;
|
|
5666
|
+
}
|
|
5667
|
+
await waitAndReport(ctx, a, resource.id, ["active"], { action: "provision" });
|
|
5668
|
+
}
|
|
5669
|
+
async function infraHibernate(ctx, a) {
|
|
5670
|
+
const id = a.req("id");
|
|
5671
|
+
const { resource } = await hibernate(ctx, { id });
|
|
5672
|
+
if (!a.bool("wait")) {
|
|
5673
|
+
out({ resource });
|
|
5674
|
+
return;
|
|
5675
|
+
}
|
|
5676
|
+
await waitAndReport(ctx, a, id, ["hibernated"], { action: "hibernate" });
|
|
5677
|
+
}
|
|
5678
|
+
async function infraResume(ctx, a) {
|
|
5679
|
+
const id = a.req("id");
|
|
5680
|
+
const { resource } = await resume(ctx, { id });
|
|
5681
|
+
if (!a.bool("wait")) {
|
|
5682
|
+
out({ resource });
|
|
5683
|
+
return;
|
|
5684
|
+
}
|
|
5685
|
+
await waitAndReport(ctx, a, id, ["active"], { action: "resume" });
|
|
5686
|
+
}
|
|
5687
|
+
async function infraDestroy(ctx, a) {
|
|
5688
|
+
const id = a.req("id");
|
|
5689
|
+
const { resource } = await destroy(ctx, { id });
|
|
5690
|
+
if (!a.bool("wait")) {
|
|
5691
|
+
out({ resource });
|
|
5692
|
+
return;
|
|
5693
|
+
}
|
|
5694
|
+
await waitAndReport(ctx, a, id, ["destroyed"], { action: "destroy" });
|
|
5695
|
+
}
|
|
5696
|
+
async function infraRename(ctx, a) {
|
|
5697
|
+
out(
|
|
5698
|
+
await rename(ctx, {
|
|
5699
|
+
id: a.req("id"),
|
|
5700
|
+
name: a.str("name")
|
|
5701
|
+
})
|
|
5702
|
+
);
|
|
5703
|
+
}
|
|
5704
|
+
var infraHandlers = {
|
|
5705
|
+
"infra list": infraList,
|
|
5706
|
+
"infra get": infraGet,
|
|
5707
|
+
"infra provision": infraProvision,
|
|
5708
|
+
"infra hibernate": infraHibernate,
|
|
5709
|
+
"infra resume": infraResume,
|
|
5710
|
+
"infra destroy": infraDestroy,
|
|
5711
|
+
"infra rename": infraRename
|
|
5712
|
+
};
|
|
5713
|
+
var infraHelp = `remy-admin infra \u2014 Dedicated infrastructure your app leases from the platform.
|
|
5714
|
+
|
|
5715
|
+
Today the one kind is dedicated retrieval: the app's own vector-store capacity
|
|
5716
|
+
for data sources, isolated from the shared pool. Sizes and prices come from the
|
|
5717
|
+
platform catalog (\`infra list\` prints them); nothing is priced client-side.
|
|
5718
|
+
|
|
5719
|
+
A resource bills by the hour from activation, at the retained-storage rate while
|
|
5720
|
+
hibernated, and not at all once destroyed. Provisioning needs a month's worth of
|
|
5721
|
+
credits available in the workspace; nothing is charged until activation. Every
|
|
5722
|
+
action below is audited.
|
|
5723
|
+
|
|
5724
|
+
Subcommands:
|
|
5725
|
+
list Resources on this app, plus the offerings you can provision
|
|
5726
|
+
get One resource with its state timeline and attached sources
|
|
5727
|
+
provision Lease a new resource (spends credits)
|
|
5728
|
+
hibernate Park it: data kept, searches paused, compute charge stops
|
|
5729
|
+
resume Bring a hibernated resource back
|
|
5730
|
+
destroy Delete it (refused while data sources are placed on it)
|
|
5731
|
+
rename Change the display name
|
|
5732
|
+
|
|
5733
|
+
Usage:
|
|
5734
|
+
remy-admin infra list [--include-destroyed]
|
|
5735
|
+
remy-admin infra get <id>
|
|
5736
|
+
remy-admin infra provision --offering <id> [--name <name>] [--wait] [--timeout <sec>]
|
|
5737
|
+
remy-admin infra hibernate <id> [--wait] [--timeout <sec>]
|
|
5738
|
+
remy-admin infra resume <id> [--wait] [--timeout <sec>]
|
|
5739
|
+
remy-admin infra destroy <id> [--wait] [--timeout <sec>]
|
|
5740
|
+
remy-admin infra rename <id> --name <name>
|
|
5741
|
+
|
|
5742
|
+
Placing a data source on a resource:
|
|
5743
|
+
remy-admin datasources create --source <slug> --placement <resource-id>
|
|
5744
|
+
remy-admin datasources config --source <slug> --placement <resource-id>
|
|
5745
|
+
remy-admin datasources config --source <slug> --placement shared
|
|
5746
|
+
|
|
5747
|
+
Start a new corpus on a resource with \`create\`; \`add\` creates a source on
|
|
5748
|
+
the shared pool. Placement can only change while the source has no built
|
|
5749
|
+
documents; moving a populated source is a re-vectorization onto the new
|
|
5750
|
+
capacity, which is not available yet. Searches on a source whose resource is
|
|
5751
|
+
not active fail with capacity_<phase>: capacity_hibernated once parked,
|
|
5752
|
+
capacity_hibernating / capacity_resuming while it moves, and
|
|
5753
|
+
capacity_restoring while a resumed instance reloads its data.
|
|
5754
|
+
|
|
5755
|
+
Phases:
|
|
5756
|
+
requested \u2192 provisioning \u2192 active \u21C4 hibernated \u2192 destroyed, with hibernating /
|
|
5757
|
+
resuming / decommissioning in between and failed when a change cannot complete
|
|
5758
|
+
(retried automatically; resume or hibernate to retry by hand).
|
|
5759
|
+
|
|
5760
|
+
--wait exit codes: 0 reached the target phase \xB7 1 the platform reported failure \xB7
|
|
5761
|
+
2 still transitioning when --timeout elapsed (default 600s).`;
|
|
5762
|
+
|
|
5336
5763
|
// src/commands/index.ts
|
|
5337
5764
|
var SPECS = {
|
|
5338
5765
|
...requestsSpecs,
|
|
@@ -5356,7 +5783,8 @@ var SPECS = {
|
|
|
5356
5783
|
...jewelsSpecs,
|
|
5357
5784
|
...settingsSpecs,
|
|
5358
5785
|
...emailSpecs,
|
|
5359
|
-
...cronSpecs
|
|
5786
|
+
...cronSpecs,
|
|
5787
|
+
...infraSpecs
|
|
5360
5788
|
};
|
|
5361
5789
|
var HANDLERS = {
|
|
5362
5790
|
...requestsHandlers,
|
|
@@ -5380,7 +5808,8 @@ var HANDLERS = {
|
|
|
5380
5808
|
...jewelsHandlers,
|
|
5381
5809
|
...settingsHandlers,
|
|
5382
5810
|
...emailHandlers,
|
|
5383
|
-
...cronHandlers
|
|
5811
|
+
...cronHandlers,
|
|
5812
|
+
...infraHandlers
|
|
5384
5813
|
};
|
|
5385
5814
|
var GROUP_HELP = {
|
|
5386
5815
|
requests: requestsHelp,
|
|
@@ -5404,7 +5833,8 @@ var GROUP_HELP = {
|
|
|
5404
5833
|
jewels: jewelsHelp,
|
|
5405
5834
|
settings: settingsHelp,
|
|
5406
5835
|
email: emailHelp,
|
|
5407
|
-
cron: cronHelp
|
|
5836
|
+
cron: cronHelp,
|
|
5837
|
+
infra: infraHelp
|
|
5408
5838
|
};
|
|
5409
5839
|
|
|
5410
5840
|
// src/ctx.ts
|
|
@@ -5517,7 +5947,8 @@ var GROUPS = [
|
|
|
5517
5947
|
"jewels",
|
|
5518
5948
|
"settings",
|
|
5519
5949
|
"email",
|
|
5520
|
-
"cron"
|
|
5950
|
+
"cron",
|
|
5951
|
+
"infra"
|
|
5521
5952
|
];
|
|
5522
5953
|
var KEYS = new Set(Object.keys(SPECS));
|
|
5523
5954
|
function resolveRoute(argv) {
|