@datarails-ai/dr-cli 1.6.149 → 1.6.151
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/README.md +2 -2
- package/dist/cli.js +1697 -1282
- package/dist/scopes-map.json +39 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import { fileURLToPath as fileURLToPath3 } from "url";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "@datarails-ai/dr-cli",
|
|
11
|
-
version: "1.6.
|
|
11
|
+
version: "1.6.151",
|
|
12
12
|
description: "Datarails CLI",
|
|
13
13
|
type: "module",
|
|
14
14
|
engines: {
|
|
@@ -40,7 +40,7 @@ var package_default = {
|
|
|
40
40
|
postinstall: "node scripts/postinstall.mjs"
|
|
41
41
|
},
|
|
42
42
|
dependencies: {
|
|
43
|
-
"@datarails-ai/dr-appspace-cli": "0.12.
|
|
43
|
+
"@datarails-ai/dr-appspace-cli": "0.12.21",
|
|
44
44
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
45
45
|
"@napi-rs/keyring": "^1.3.0",
|
|
46
46
|
commander: "^12.1.0",
|
|
@@ -69,7 +69,7 @@ var package_default = {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
// src/buildInfo.ts
|
|
72
|
-
var BUILD_TIME = "2026-10-
|
|
72
|
+
var BUILD_TIME = "2026-10-22T09:31:33.112Z";
|
|
73
73
|
function formatBuildTime(iso) {
|
|
74
74
|
if (iso === "") return "";
|
|
75
75
|
const d = new Date(iso);
|
|
@@ -512,6 +512,12 @@ function resolve(input) {
|
|
|
512
512
|
return lookup(trimmed);
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
+
// src/auth/userAgent.ts
|
|
516
|
+
var USER_AGENT = `dr-cli/${package_default.version} (${process.platform}; ${process.arch}; node/${process.versions.node})`;
|
|
517
|
+
function withUserAgent(headers) {
|
|
518
|
+
return { "User-Agent": USER_AGENT, ...headers };
|
|
519
|
+
}
|
|
520
|
+
|
|
515
521
|
// src/auth/errors.ts
|
|
516
522
|
var NotLoggedInError = class extends Error {
|
|
517
523
|
code = "ERR_NOT_LOGGED_IN";
|
|
@@ -760,12 +766,12 @@ async function mintJwt(host, sessionId, csrfToken, options) {
|
|
|
760
766
|
const base = trimTrailingSlash(host);
|
|
761
767
|
const res = await fetch(`${base}/jwt/api/token/`, {
|
|
762
768
|
method: "POST",
|
|
763
|
-
headers: {
|
|
769
|
+
headers: withUserAgent({
|
|
764
770
|
"Content-Type": "application/json",
|
|
765
771
|
Cookie: `sessionid=${sessionId}; csrftoken=${csrfToken}`,
|
|
766
772
|
"X-CSRFToken": csrfToken,
|
|
767
773
|
Referer: `${base}/`
|
|
768
|
-
},
|
|
774
|
+
}),
|
|
769
775
|
body: "{}",
|
|
770
776
|
signal: options?.signal
|
|
771
777
|
});
|
|
@@ -789,10 +795,10 @@ async function refreshJwt(host, refreshToken, options) {
|
|
|
789
795
|
const base = trimTrailingSlash(host);
|
|
790
796
|
const res = await fetch(`${base}/jwt/api/token/refresh/`, {
|
|
791
797
|
method: "POST",
|
|
792
|
-
headers: {
|
|
798
|
+
headers: withUserAgent({
|
|
793
799
|
"Content-Type": "application/json",
|
|
794
800
|
Referer: `${base}/`
|
|
795
|
-
},
|
|
801
|
+
}),
|
|
796
802
|
body: JSON.stringify({ refresh: refreshToken }),
|
|
797
803
|
signal: options?.signal
|
|
798
804
|
});
|
|
@@ -816,7 +822,7 @@ async function refreshExternalJwt(host, refreshToken, options) {
|
|
|
816
822
|
const base = trimTrailingSlash(host);
|
|
817
823
|
const res = await fetch(`${base}/jwt/api/external/token/refresh/`, {
|
|
818
824
|
method: "POST",
|
|
819
|
-
headers: { "Content-Type": "application/json" },
|
|
825
|
+
headers: withUserAgent({ "Content-Type": "application/json" }),
|
|
820
826
|
body: JSON.stringify({ refresh: refreshToken }),
|
|
821
827
|
signal: options?.signal
|
|
822
828
|
});
|
|
@@ -855,7 +861,7 @@ async function exchangeApiKey(host, apiKey, options) {
|
|
|
855
861
|
const base = trimTrailingSlash(host);
|
|
856
862
|
const res = await fetch(`${base}/jwt/api/apikey/token/`, {
|
|
857
863
|
method: "POST",
|
|
858
|
-
headers: { Authorization: `Bearer ${key}`, Accept: "application/json" },
|
|
864
|
+
headers: withUserAgent({ Authorization: `Bearer ${key}`, Accept: "application/json" }),
|
|
859
865
|
signal: options?.signal
|
|
860
866
|
});
|
|
861
867
|
const text2 = await res.text();
|
|
@@ -912,10 +918,10 @@ async function loginAs(host, operatorAccessToken, userId, options) {
|
|
|
912
918
|
}
|
|
913
919
|
const res = await fetch(`${base}/jwt/api/loginas/`, {
|
|
914
920
|
method: "POST",
|
|
915
|
-
headers: {
|
|
921
|
+
headers: withUserAgent({
|
|
916
922
|
"Content-Type": "application/json",
|
|
917
923
|
Authorization: `Bearer ${operatorAccessToken}`
|
|
918
|
-
},
|
|
924
|
+
}),
|
|
919
925
|
body: JSON.stringify(body),
|
|
920
926
|
signal: options?.signal
|
|
921
927
|
});
|
|
@@ -1075,7 +1081,7 @@ function exchangeForSession(store, serverKey, email, session, host, opts) {
|
|
|
1075
1081
|
const deadline = AbortSignal.timeout(API_KEY_EXCHANGE_DEADLINE_MS);
|
|
1076
1082
|
deadline.addEventListener("abort", () => controller.abort(deadline.reason), { once: true });
|
|
1077
1083
|
const signal = controller.signal;
|
|
1078
|
-
const
|
|
1084
|
+
const run2 = (async () => {
|
|
1079
1085
|
if (host === "") {
|
|
1080
1086
|
throw new SessionExpiredError(`${identity2} (no host to exchange the API key against)`);
|
|
1081
1087
|
}
|
|
@@ -1148,20 +1154,20 @@ function exchangeForSession(store, serverKey, email, session, host, opts) {
|
|
|
1148
1154
|
}
|
|
1149
1155
|
})();
|
|
1150
1156
|
const evict = () => {
|
|
1151
|
-
if (inFlightExchanges.get(key)?.run ===
|
|
1157
|
+
if (inFlightExchanges.get(key)?.run === run2) inFlightExchanges.delete(key);
|
|
1152
1158
|
};
|
|
1153
1159
|
const entry = {
|
|
1154
|
-
run,
|
|
1160
|
+
run: run2,
|
|
1155
1161
|
waiters: 0,
|
|
1156
1162
|
abandon: () => {
|
|
1157
|
-
if (inFlightExchanges.get(key)?.run !==
|
|
1163
|
+
if (inFlightExchanges.get(key)?.run !== run2) return;
|
|
1158
1164
|
evict();
|
|
1159
1165
|
const reason = new Error(`${identity2} (api-key exchange abandoned by its last caller)`);
|
|
1160
1166
|
reason.name = "AbortError";
|
|
1161
1167
|
controller.abort(reason);
|
|
1162
1168
|
}
|
|
1163
1169
|
};
|
|
1164
|
-
void
|
|
1170
|
+
void run2.then(evict, evict);
|
|
1165
1171
|
inFlightExchanges.set(key, entry);
|
|
1166
1172
|
return joinExchange(entry, opts.signal);
|
|
1167
1173
|
}
|
|
@@ -2150,6 +2156,7 @@ var AuthorizedClient = class _AuthorizedClient {
|
|
|
2150
2156
|
* @internal
|
|
2151
2157
|
*/
|
|
2152
2158
|
applyAuthHeaders(headers, hasBody, includeBearer) {
|
|
2159
|
+
headers.set("User-Agent", USER_AGENT);
|
|
2153
2160
|
if (!this.isCookieless()) {
|
|
2154
2161
|
headers.set(
|
|
2155
2162
|
"Cookie",
|
|
@@ -5607,11 +5614,11 @@ async function endCookieSession(base, session) {
|
|
|
5607
5614
|
return res.status >= 200 && res.status < 400 ? void 0 : `HTTP ${res.status}`;
|
|
5608
5615
|
});
|
|
5609
5616
|
}
|
|
5610
|
-
async function withTimeout(
|
|
5617
|
+
async function withTimeout(run2) {
|
|
5611
5618
|
const controller = new AbortController();
|
|
5612
5619
|
const timeout = setTimeout(() => controller.abort(), REVOKE_TIMEOUT_MS);
|
|
5613
5620
|
try {
|
|
5614
|
-
return await
|
|
5621
|
+
return await run2(controller.signal);
|
|
5615
5622
|
} catch {
|
|
5616
5623
|
return controller.signal.aborted ? "timed out" : "network error";
|
|
5617
5624
|
} finally {
|
|
@@ -5652,7 +5659,7 @@ async function requestDeviceCode(host, options) {
|
|
|
5652
5659
|
const body = new URLSearchParams({ client_id: CLIENT_ID, scope: paths.scope });
|
|
5653
5660
|
const res = await fetch(`${base}${paths.code}`, {
|
|
5654
5661
|
method: "POST",
|
|
5655
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
5662
|
+
headers: withUserAgent({ "Content-Type": "application/x-www-form-urlencoded" }),
|
|
5656
5663
|
body: body.toString(),
|
|
5657
5664
|
signal: options?.signal
|
|
5658
5665
|
});
|
|
@@ -5780,7 +5787,7 @@ async function poll(host, path13, deviceCode, options, terminalHints, onSuccess)
|
|
|
5780
5787
|
});
|
|
5781
5788
|
const res = await fetch(`${base}${path13}`, {
|
|
5782
5789
|
method: "POST",
|
|
5783
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
5790
|
+
headers: withUserAgent({ "Content-Type": "application/x-www-form-urlencoded" }),
|
|
5784
5791
|
body: body.toString(),
|
|
5785
5792
|
signal
|
|
5786
5793
|
});
|
|
@@ -25922,14 +25929,14 @@ function wireDimensionSuggestions(dimensions) {
|
|
|
25922
25929
|
refresh: runDimensionSuggestionsRefresh
|
|
25923
25930
|
});
|
|
25924
25931
|
}
|
|
25925
|
-
function wireSuggestionGroup(parent, r,
|
|
25932
|
+
function wireSuggestionGroup(parent, r, run2) {
|
|
25926
25933
|
const group = parent.command("suggestions").description(
|
|
25927
25934
|
`AI-suggested ${r.noun}s \u2014 agent-generated; requires the ${r.flagName} Unleash flag and an org-admin user. \`profile\` is the exception: read-only table/field profiling, no flag needed`
|
|
25928
25935
|
);
|
|
25929
25936
|
const listCmd = group.command("list").description(`List AI ${r.noun} suggestions and the latest generation-run status (last_run)`).option("--json", "Output raw JSON instead of a table", false).action(async (opts) => {
|
|
25930
25937
|
const setup = await setupCall(opts.server, opts.account);
|
|
25931
25938
|
try {
|
|
25932
|
-
await
|
|
25939
|
+
await run2.list({
|
|
25933
25940
|
client: setup.client,
|
|
25934
25941
|
asJson: opts.json === true,
|
|
25935
25942
|
signal: setup.signal,
|
|
@@ -25959,7 +25966,7 @@ function wireSuggestionGroup(parent, r, run) {
|
|
|
25959
25966
|
timeoutMs: suggestionsCallTimeoutMs(wait, timeoutSec)
|
|
25960
25967
|
});
|
|
25961
25968
|
try {
|
|
25962
|
-
await
|
|
25969
|
+
await run2.refresh({
|
|
25963
25970
|
client: setup.client,
|
|
25964
25971
|
asJson: opts.json === true,
|
|
25965
25972
|
wait,
|
|
@@ -31281,16 +31288,16 @@ async function runEnrichmentTypesList(cfg) {
|
|
|
31281
31288
|
function oneLine2(s, max) {
|
|
31282
31289
|
return truncate3(s.replace(/\s+/g, " "), max);
|
|
31283
31290
|
}
|
|
31284
|
-
async function runBatch(items, target,
|
|
31291
|
+
async function runBatch(items, target, run2, signal) {
|
|
31285
31292
|
if (items.length === 1) {
|
|
31286
31293
|
const only = items[0];
|
|
31287
|
-
return [{ target: target(only, 0), ok: true, value: await
|
|
31294
|
+
return [{ target: target(only, 0), ok: true, value: await run2(only) }];
|
|
31288
31295
|
}
|
|
31289
31296
|
const outcomes = [];
|
|
31290
31297
|
for (let i = 0; i < items.length; i++) {
|
|
31291
31298
|
const item = items[i];
|
|
31292
31299
|
try {
|
|
31293
|
-
outcomes.push({ target: target(item, i), ok: true, value: await
|
|
31300
|
+
outcomes.push({ target: target(item, i), ok: true, value: await run2(item) });
|
|
31294
31301
|
} catch (err) {
|
|
31295
31302
|
outcomes.push({
|
|
31296
31303
|
target: target(item, i),
|
|
@@ -33227,16 +33234,16 @@ function findArityViolations(entries) {
|
|
|
33227
33234
|
for (const entry of entries) {
|
|
33228
33235
|
if (typeof entry.formula !== "string") continue;
|
|
33229
33236
|
const calls = findFunctionCalls(entry.formula);
|
|
33230
|
-
for (const
|
|
33231
|
-
const upper =
|
|
33237
|
+
for (const call3 of calls) {
|
|
33238
|
+
const upper = call3.name.toUpperCase();
|
|
33232
33239
|
const arity = KNOWN_FUNCTION_ARITIES[upper];
|
|
33233
33240
|
if (arity === void 0) continue;
|
|
33234
|
-
if (
|
|
33241
|
+
if (call3.argCount >= arity.min && call3.argCount <= arity.max) continue;
|
|
33235
33242
|
const expected = arity.min === arity.max ? `exactly ${arity.min}` : `${arity.min}-${arity.max}`;
|
|
33236
33243
|
violations.push({
|
|
33237
33244
|
fieldName: entry.name === "" ? "(unnamed)" : entry.name,
|
|
33238
|
-
functionName:
|
|
33239
|
-
got:
|
|
33245
|
+
functionName: call3.name,
|
|
33246
|
+
got: call3.argCount,
|
|
33240
33247
|
expected,
|
|
33241
33248
|
note: arity.note
|
|
33242
33249
|
});
|
|
@@ -33373,13 +33380,20 @@ import { promises as fs16 } from "fs";
|
|
|
33373
33380
|
import * as os3 from "os";
|
|
33374
33381
|
import * as path7 from "path";
|
|
33375
33382
|
var RESPONSE_BODY_MAX_BYTES = 8 * 1024;
|
|
33383
|
+
var MAX_NAME_SUFFIX = 99;
|
|
33376
33384
|
async function writeErrorLog(opts) {
|
|
33377
|
-
const
|
|
33385
|
+
const stamp2 = timestampStamp();
|
|
33378
33386
|
const dir = await chooseDir(opts.dirOverride);
|
|
33379
|
-
const filePath = path7.join(dir, filename);
|
|
33380
33387
|
const md = formatLog(opts);
|
|
33381
|
-
|
|
33382
|
-
|
|
33388
|
+
for (let n = 0; ; n++) {
|
|
33389
|
+
const filePath = path7.join(dir, `dr-cli-error-${stamp2}${n === 0 ? "" : `-${n}`}.md`);
|
|
33390
|
+
try {
|
|
33391
|
+
await fs16.writeFile(filePath, md, { encoding: "utf8", mode: 384, flag: "wx" });
|
|
33392
|
+
return filePath;
|
|
33393
|
+
} catch (err) {
|
|
33394
|
+
if (err.code !== "EEXIST" || n >= MAX_NAME_SUFFIX) throw err;
|
|
33395
|
+
}
|
|
33396
|
+
}
|
|
33383
33397
|
}
|
|
33384
33398
|
function timestampStamp() {
|
|
33385
33399
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}/, "");
|
|
@@ -46207,557 +46221,1248 @@ function toControlPlaneError(method, path13, err) {
|
|
|
46207
46221
|
);
|
|
46208
46222
|
}
|
|
46209
46223
|
|
|
46210
|
-
// src/
|
|
46211
|
-
|
|
46212
|
-
|
|
46213
|
-
|
|
46214
|
-
|
|
46215
|
-
|
|
46216
|
-
|
|
46217
|
-
...i.queryConfig,
|
|
46218
|
-
// Coerce absent values to null (not undefined) so JSON.stringify keeps the
|
|
46219
|
-
// key — matches build_hook_job's dq.get(...) -> None; a dropped objectType
|
|
46220
|
-
// becomes a cryptic KeyError in the runner's dispatch.
|
|
46221
|
-
objectType: i.dataQuery.objectType ?? null,
|
|
46222
|
-
type: i.dataQuery.type ?? null,
|
|
46223
|
-
query: i.dataQuery.query
|
|
46224
|
-
};
|
|
46225
|
-
if (i.period) config.period = i.period;
|
|
46226
|
-
return {
|
|
46227
|
-
correlation_id: i.correlationId,
|
|
46228
|
-
config,
|
|
46229
|
-
secret_ref: i.secretRef,
|
|
46230
|
-
cred_token: "dev-local",
|
|
46231
|
-
output_sas: "file:///out/output.csv",
|
|
46232
|
-
input_sas: null,
|
|
46233
|
-
cursor: null,
|
|
46234
|
-
contract_version: i.contractVersion,
|
|
46235
|
-
metadata: {}
|
|
46236
|
-
};
|
|
46237
|
-
}
|
|
46238
|
-
function asObject(v) {
|
|
46239
|
-
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
46240
|
-
return v;
|
|
46224
|
+
// src/api/awsConnections.ts
|
|
46225
|
+
async function call(method, path13, send, what) {
|
|
46226
|
+
let out;
|
|
46227
|
+
try {
|
|
46228
|
+
out = await send();
|
|
46229
|
+
} catch (err) {
|
|
46230
|
+
throw toControlPlaneError(method, path13, err);
|
|
46241
46231
|
}
|
|
46242
|
-
|
|
46232
|
+
if (out === null) throw new Error(`${method} ${path13}: empty response from control-plane ${what}`);
|
|
46233
|
+
return out;
|
|
46243
46234
|
}
|
|
46244
|
-
function
|
|
46245
|
-
const
|
|
46246
|
-
|
|
46247
|
-
|
|
46248
|
-
|
|
46249
|
-
)
|
|
46250
|
-
|
|
46251
|
-
|
|
46252
|
-
const src = cfg ?? root;
|
|
46253
|
-
const query = src.query;
|
|
46254
|
-
let queryObj;
|
|
46255
|
-
if (query === void 0 || query === null) {
|
|
46256
|
-
queryObj = {};
|
|
46257
|
-
} else {
|
|
46258
|
-
const asObj = asObject(query);
|
|
46259
|
-
if (asObj === null) {
|
|
46260
|
-
throw new Error(
|
|
46261
|
-
`data-query \`query\` is not an object (got ${typeof query}) \u2014 expected the Sync-query field`
|
|
46262
|
-
);
|
|
46263
|
-
}
|
|
46264
|
-
queryObj = asObj;
|
|
46265
|
-
}
|
|
46266
|
-
return { objectType: queryObj.objectType, type: src.type, query: queryObj };
|
|
46235
|
+
async function createAwsConnection(client, fields, reqOptions) {
|
|
46236
|
+
const path13 = "/aws-connections";
|
|
46237
|
+
return call(
|
|
46238
|
+
"POST",
|
|
46239
|
+
path13,
|
|
46240
|
+
() => client.postJSON(path13, fields, reqOptions),
|
|
46241
|
+
"aws connection create"
|
|
46242
|
+
);
|
|
46267
46243
|
}
|
|
46268
|
-
function
|
|
46269
|
-
const
|
|
46270
|
-
|
|
46271
|
-
|
|
46272
|
-
|
|
46273
|
-
)
|
|
46274
|
-
|
|
46275
|
-
|
|
46244
|
+
async function listAwsConnections(client, secretRef, reqOptions) {
|
|
46245
|
+
const path13 = secretRef === void 0 ? "/aws-connections" : `/aws-connections?secret_ref=${encodeURIComponent(secretRef)}`;
|
|
46246
|
+
return call(
|
|
46247
|
+
"GET",
|
|
46248
|
+
path13,
|
|
46249
|
+
() => client.getJSON(path13, reqOptions),
|
|
46250
|
+
"aws connection list"
|
|
46251
|
+
);
|
|
46276
46252
|
}
|
|
46277
|
-
|
|
46278
|
-
|
|
46279
|
-
|
|
46280
|
-
|
|
46281
|
-
|
|
46282
|
-
|
|
46283
|
-
|
|
46284
|
-
|
|
46285
|
-
|
|
46286
|
-
|
|
46287
|
-
}
|
|
46288
|
-
|
|
46253
|
+
async function getAwsConnection(client, id, reqOptions) {
|
|
46254
|
+
const path13 = `/aws-connections/${id}`;
|
|
46255
|
+
return call(
|
|
46256
|
+
"GET",
|
|
46257
|
+
path13,
|
|
46258
|
+
() => client.getJSON(path13, reqOptions),
|
|
46259
|
+
"aws connection get"
|
|
46260
|
+
);
|
|
46261
|
+
}
|
|
46262
|
+
async function updateAwsConnection(client, id, fields, reqOptions) {
|
|
46263
|
+
const path13 = `/aws-connections/${id}`;
|
|
46264
|
+
return call(
|
|
46265
|
+
"PATCH",
|
|
46266
|
+
path13,
|
|
46267
|
+
() => client.patchJSON(path13, fields, reqOptions),
|
|
46268
|
+
"aws connection update"
|
|
46269
|
+
);
|
|
46270
|
+
}
|
|
46271
|
+
|
|
46272
|
+
// src/commands/connectorsTasks.ts
|
|
46273
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
46274
|
+
|
|
46275
|
+
// src/api/scheduledTasks.ts
|
|
46276
|
+
var BASE2 = "/scheduled-tasks";
|
|
46277
|
+
async function call2(method, path13, send, what) {
|
|
46278
|
+
let out;
|
|
46289
46279
|
try {
|
|
46290
|
-
|
|
46291
|
-
} catch {
|
|
46292
|
-
throw
|
|
46280
|
+
out = await send();
|
|
46281
|
+
} catch (err) {
|
|
46282
|
+
throw toControlPlaneError(method, path13, err);
|
|
46293
46283
|
}
|
|
46294
|
-
|
|
46295
|
-
|
|
46296
|
-
|
|
46297
|
-
|
|
46298
|
-
|
|
46284
|
+
if (out === null) throw new Error(`${method} ${path13}: empty response from control-plane ${what}`);
|
|
46285
|
+
return out;
|
|
46286
|
+
}
|
|
46287
|
+
async function createTask(client, fields, reqOptions) {
|
|
46288
|
+
return call2(
|
|
46289
|
+
"POST",
|
|
46290
|
+
BASE2,
|
|
46291
|
+
() => client.postJSON(BASE2, fields, reqOptions),
|
|
46292
|
+
"task create"
|
|
46299
46293
|
);
|
|
46300
|
-
|
|
46301
|
-
|
|
46302
|
-
|
|
46303
|
-
|
|
46304
|
-
|
|
46294
|
+
}
|
|
46295
|
+
async function listTasks(client, orgId, reqOptions) {
|
|
46296
|
+
const path13 = orgId === void 0 ? BASE2 : `${BASE2}?org_id=${orgId}`;
|
|
46297
|
+
return call2("GET", path13, () => client.getJSON(path13, reqOptions), "task list");
|
|
46298
|
+
}
|
|
46299
|
+
async function getTask(client, taskId, reqOptions) {
|
|
46300
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
46301
|
+
return call2("GET", path13, () => client.getJSON(path13, reqOptions), "task get");
|
|
46302
|
+
}
|
|
46303
|
+
async function updateTask(client, taskId, fields, reqOptions) {
|
|
46304
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
46305
|
+
return call2(
|
|
46306
|
+
"PATCH",
|
|
46307
|
+
path13,
|
|
46308
|
+
() => client.patchJSON(path13, fields, reqOptions),
|
|
46309
|
+
"task update"
|
|
46305
46310
|
);
|
|
46306
|
-
if (process.platform !== "win32") {
|
|
46307
|
-
await fs19.chmod(secretsPath, 384);
|
|
46308
|
-
}
|
|
46309
|
-
await fs19.writeFile(path9.join(input.outDir, CONNECTION_FILE), connectionSource, "utf-8");
|
|
46310
|
-
await fs19.writeFile(path9.join(input.outDir, QUERY_FILE), querySource, "utf-8");
|
|
46311
46311
|
}
|
|
46312
|
-
async function
|
|
46313
|
-
const
|
|
46314
|
-
let current = "";
|
|
46312
|
+
async function deleteTask(client, taskId, reqOptions) {
|
|
46313
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
46315
46314
|
try {
|
|
46316
|
-
|
|
46317
|
-
} catch {
|
|
46315
|
+
await client.deleteJSON(path13, reqOptions);
|
|
46316
|
+
} catch (err) {
|
|
46317
|
+
throw toControlPlaneError("DELETE", path13, err);
|
|
46318
46318
|
}
|
|
46319
|
-
const normalized = entry.split("\\").join("/").replace(/\/+$/, "") + "/";
|
|
46320
|
-
const lines = current.split(/\r?\n/).map((l) => l.trim());
|
|
46321
|
-
if (lines.includes(normalized) || lines.includes(entry)) return;
|
|
46322
|
-
const sep = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
46323
|
-
await fs19.writeFile(gitignorePath, `${current}${sep}${normalized}
|
|
46324
|
-
`, "utf-8");
|
|
46325
46319
|
}
|
|
46326
|
-
function
|
|
46327
|
-
const
|
|
46328
|
-
|
|
46329
|
-
|
|
46330
|
-
|
|
46331
|
-
|
|
46320
|
+
async function runTask(client, taskId, reqOptions) {
|
|
46321
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/run`;
|
|
46322
|
+
return call2(
|
|
46323
|
+
"POST",
|
|
46324
|
+
path13,
|
|
46325
|
+
() => client.postJSON(path13, {}, reqOptions),
|
|
46326
|
+
"task run"
|
|
46327
|
+
);
|
|
46332
46328
|
}
|
|
46333
|
-
function
|
|
46334
|
-
const
|
|
46335
|
-
|
|
46336
|
-
|
|
46337
|
-
|
|
46338
|
-
|
|
46339
|
-
|
|
46340
|
-
|
|
46341
|
-
|
|
46342
|
-
|
|
46343
|
-
|
|
46344
|
-
|
|
46345
|
-
|
|
46346
|
-
|
|
46347
|
-
}
|
|
46348
|
-
|
|
46349
|
-
|
|
46350
|
-
|
|
46351
|
-
|
|
46352
|
-
|
|
46353
|
-
|
|
46354
|
-
|
|
46355
|
-
|
|
46356
|
-
|
|
46357
|
-
|
|
46358
|
-
});
|
|
46359
|
-
const monoSetup = await setupCall(opts.server, opts.account, {
|
|
46360
|
-
host: monolithHost,
|
|
46361
|
-
authHost: monolithHost
|
|
46362
|
-
});
|
|
46363
|
-
try {
|
|
46364
|
-
const rawDataQuery = await getDataQueryRaw(monoSetup.client, documentId, {
|
|
46365
|
-
signal: monoSetup.signal
|
|
46366
|
-
});
|
|
46367
|
-
const dataQuery = extractDataQueryFields(rawDataQuery);
|
|
46368
|
-
if (dataQuery.objectType === void 0 || dataQuery.objectType === null || dataQuery.objectType === "") {
|
|
46369
|
-
process.stderr.write(
|
|
46370
|
-
'WARNING: objectType is not set inside the Sync-query (config.query.objectType). get_query_class will receive None \u2014 fine only if the connector has a single default dataset. Add it to the data-query, e.g. config.query.objectType = "<dataset>".\n'
|
|
46371
|
-
);
|
|
46372
|
-
}
|
|
46373
|
-
const integration = await itGet(monoSetup.client, datasourceId, {
|
|
46374
|
-
signal: monoSetup.signal
|
|
46375
|
-
});
|
|
46376
|
-
const credentials = extractCredentials(integration);
|
|
46377
|
-
let period;
|
|
46378
|
-
if (opts.from !== void 0 && opts.to !== void 0) {
|
|
46379
|
-
period = { from: opts.from, to: opts.to };
|
|
46380
|
-
}
|
|
46381
|
-
const env = buildDevEnvelope({
|
|
46382
|
-
correlationId: randomUUID2(),
|
|
46383
|
-
secretRef: binding.secret_ref,
|
|
46384
|
-
publicConfig: binding.public_config,
|
|
46385
|
-
queryConfig: binding.query_config,
|
|
46386
|
-
dataQuery,
|
|
46387
|
-
...period !== void 0 ? { period } : {},
|
|
46388
|
-
// Deliberate sandbox stub: the fetch/dispatch path doesn't branch on
|
|
46389
|
-
// contract_version, so a fixed "1" is sufficient for a local run.
|
|
46390
|
-
contractVersion: "1"
|
|
46391
|
-
});
|
|
46392
|
-
await writeRunDir({
|
|
46393
|
-
outDir,
|
|
46394
|
-
env,
|
|
46395
|
-
secretRef: binding.secret_ref,
|
|
46396
|
-
credentials,
|
|
46397
|
-
connectionPath: opts.connection,
|
|
46398
|
-
queryPath: opts.query
|
|
46399
|
-
});
|
|
46400
|
-
await ensureGitignored(gitignoreEntry);
|
|
46401
|
-
process.stdout.write(`Wrote run dir: ${outDir}
|
|
46402
|
-
`);
|
|
46403
|
-
process.stdout.write(
|
|
46404
|
-
` env.json (secret_ref=${binding.secret_ref}, flavor=${binding.runtime_flavor})
|
|
46405
|
-
`
|
|
46406
|
-
);
|
|
46407
|
-
process.stdout.write(" secrets.json (credentials NOT shown)\n");
|
|
46408
|
-
process.stdout.write(" connection.py, query.py\n");
|
|
46409
|
-
} finally {
|
|
46410
|
-
monoSetup.cleanup();
|
|
46411
|
-
}
|
|
46412
|
-
} catch (err) {
|
|
46413
|
-
throw augmentTlsCertError(err);
|
|
46414
|
-
} finally {
|
|
46415
|
-
cpSetup.cleanup();
|
|
46416
|
-
}
|
|
46417
|
-
});
|
|
46418
|
-
addServerFlag(cmd);
|
|
46419
|
-
addAccountFlag(cmd);
|
|
46420
|
-
cmd.option(
|
|
46421
|
-
"--connectors-url <url>",
|
|
46422
|
-
"Override the control-plane base URL (e.g. a kubectl port-forward http://localhost:8000)"
|
|
46329
|
+
async function pauseTask(client, taskId, reqOptions) {
|
|
46330
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/pause`;
|
|
46331
|
+
return call2(
|
|
46332
|
+
"POST",
|
|
46333
|
+
path13,
|
|
46334
|
+
() => client.postJSON(path13, {}, reqOptions),
|
|
46335
|
+
"task pause"
|
|
46336
|
+
);
|
|
46337
|
+
}
|
|
46338
|
+
async function resumeTask(client, taskId, reqOptions) {
|
|
46339
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/resume`;
|
|
46340
|
+
return call2(
|
|
46341
|
+
"POST",
|
|
46342
|
+
path13,
|
|
46343
|
+
() => client.postJSON(path13, {}, reqOptions),
|
|
46344
|
+
"task resume"
|
|
46345
|
+
);
|
|
46346
|
+
}
|
|
46347
|
+
async function listRuns(client, taskId, limit, reqOptions) {
|
|
46348
|
+
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/runs?limit=${limit}`;
|
|
46349
|
+
return call2(
|
|
46350
|
+
"GET",
|
|
46351
|
+
path13,
|
|
46352
|
+
() => client.getJSON(path13, reqOptions),
|
|
46353
|
+
"task runs"
|
|
46423
46354
|
);
|
|
46424
46355
|
}
|
|
46425
46356
|
|
|
46426
|
-
// src/commands/
|
|
46427
|
-
import * as readline2 from "readline";
|
|
46428
|
-
import { createHash as createHash11 } from "crypto";
|
|
46429
|
-
|
|
46430
|
-
// src/types/connectors.ts
|
|
46431
|
-
var POLL_ITEM_STATES = [
|
|
46432
|
-
"seen",
|
|
46433
|
-
"new",
|
|
46434
|
-
"dispatched",
|
|
46435
|
-
"running",
|
|
46436
|
-
"retry_wait",
|
|
46437
|
-
"landed",
|
|
46438
|
-
"failed",
|
|
46439
|
-
"skipped",
|
|
46440
|
-
"seeded",
|
|
46441
|
-
"superseded"
|
|
46442
|
-
];
|
|
46443
|
-
|
|
46444
|
-
// src/commands/connectorsPoll.ts
|
|
46357
|
+
// src/commands/connectorsTasks.ts
|
|
46445
46358
|
var DURATION_UNIT_SECONDS = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
46446
|
-
var
|
|
46447
|
-
|
|
46448
|
-
|
|
46449
|
-
};
|
|
46450
|
-
var LOOKBACK_MAX_DAYS = 3650;
|
|
46451
|
-
function parseDurationSeconds(raw2, flag) {
|
|
46359
|
+
var MIN_EVERY_SECONDS = 60;
|
|
46360
|
+
var MAX_EVERY_SECONDS = 2 ** 31 - 1;
|
|
46361
|
+
function parseEvery(raw2) {
|
|
46452
46362
|
const m = /^(\d+)([smhd])?$/.exec(raw2.trim());
|
|
46453
46363
|
const seconds = m === null ? NaN : Number(m[1]) * (DURATION_UNIT_SECONDS[m[2] ?? "s"] ?? 1);
|
|
46454
|
-
|
|
46455
|
-
|
|
46456
|
-
|
|
46457
|
-
|
|
46458
|
-
if (!Number.isSafeInteger(seconds) || seconds < min || seconds > max) {
|
|
46459
|
-
throw new Error(
|
|
46460
|
-
`invalid ${flag} "${raw2}": must be between ${formatSeconds(min)} and ${formatSeconds(max)}`
|
|
46461
|
-
);
|
|
46364
|
+
if (m === null)
|
|
46365
|
+
throw new Error(`invalid --every "${raw2}": expected a duration like 5m, 1h or 1d`);
|
|
46366
|
+
if (!Number.isSafeInteger(seconds) || seconds < MIN_EVERY_SECONDS || seconds > MAX_EVERY_SECONDS) {
|
|
46367
|
+
throw new Error(`invalid --every "${raw2}": must be at least 1m`);
|
|
46462
46368
|
}
|
|
46463
46369
|
return seconds;
|
|
46464
46370
|
}
|
|
46465
|
-
function
|
|
46466
|
-
const
|
|
46467
|
-
|
|
46468
|
-
|
|
46469
|
-
throw new Error(
|
|
46470
|
-
`invalid --lookback "${raw2}": must be between 1 and ${LOOKBACK_MAX_DAYS} days, e.g. 180d`
|
|
46471
|
-
);
|
|
46371
|
+
function parseCron(raw2) {
|
|
46372
|
+
const fields = raw2.trim().split(/\s+/);
|
|
46373
|
+
if (fields.length !== 5 || fields.some((f) => f === "")) {
|
|
46374
|
+
throw new Error(`invalid --cron "${raw2}": expected five fields, e.g. "15 * * * *"`);
|
|
46472
46375
|
}
|
|
46473
|
-
return
|
|
46474
|
-
}
|
|
46475
|
-
var DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
46476
|
-
var DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-](\d{2}):(\d{2}))$/;
|
|
46477
|
-
function inRange(value, max, min = 0) {
|
|
46478
|
-
const n = Number(value ?? "0");
|
|
46479
|
-
return n >= min && n <= max;
|
|
46376
|
+
return fields.join(" ");
|
|
46480
46377
|
}
|
|
46481
|
-
|
|
46482
|
-
|
|
46483
|
-
|
|
46484
|
-
|
|
46378
|
+
var ROLE_RE = /^[a-z][a-z_]*$/;
|
|
46379
|
+
var DATASOURCE_ID_RE = /^[0-9]{1,18}$/;
|
|
46380
|
+
function parseDatasources(values) {
|
|
46381
|
+
const out = {};
|
|
46382
|
+
for (const raw2 of values) {
|
|
46383
|
+
const eq = raw2.indexOf("=");
|
|
46384
|
+
const role = eq < 0 ? "" : raw2.slice(0, eq).trim();
|
|
46385
|
+
const id = eq < 0 ? "" : raw2.slice(eq + 1).trim();
|
|
46386
|
+
if (!ROLE_RE.test(role) || !DATASOURCE_ID_RE.test(id)) {
|
|
46387
|
+
throw new Error(
|
|
46388
|
+
`invalid --datasource "${raw2}": expected <role>=<datasource id>, e.g. target=4242 (role: lowercase letters and _; id: digits)`
|
|
46389
|
+
);
|
|
46390
|
+
}
|
|
46391
|
+
if (Object.hasOwn(out, role)) throw new Error(`--datasource role "${role}" given twice`);
|
|
46392
|
+
out[role] = id;
|
|
46393
|
+
}
|
|
46394
|
+
const count = Object.keys(out).length;
|
|
46395
|
+
if (count < 1 || count > 4) throw new Error("give between 1 and 4 --datasource roles");
|
|
46396
|
+
return out;
|
|
46485
46397
|
}
|
|
46486
|
-
function
|
|
46487
|
-
const
|
|
46488
|
-
|
|
46489
|
-
|
|
46490
|
-
|
|
46491
|
-
|
|
46492
|
-
|
|
46493
|
-
|
|
46494
|
-
return (/* @__PURE__ */ new Date(`${value}T00:00:00Z`)).toISOString();
|
|
46398
|
+
function parseConfig(raw2, readFile9 = readUtf8) {
|
|
46399
|
+
const text2 = raw2.startsWith("@") ? readFile9(raw2.slice(1)) : raw2;
|
|
46400
|
+
let parsed2;
|
|
46401
|
+
try {
|
|
46402
|
+
parsed2 = JSON.parse(text2);
|
|
46403
|
+
} catch (err) {
|
|
46404
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
46405
|
+
throw new Error(`invalid --config JSON: ${msg}`);
|
|
46495
46406
|
}
|
|
46496
|
-
|
|
46497
|
-
|
|
46498
|
-
|
|
46499
|
-
|
|
46500
|
-
return new Date(ms).toISOString();
|
|
46407
|
+
if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2)) {
|
|
46408
|
+
throw new Error("invalid --config: expected a JSON object");
|
|
46409
|
+
}
|
|
46410
|
+
return parsed2;
|
|
46501
46411
|
}
|
|
46502
|
-
function
|
|
46503
|
-
|
|
46504
|
-
if (raw2 === "ignore") return "ignore";
|
|
46505
|
-
throw new Error(`--same-name must be new-version or ignore (got "${raw2}")`);
|
|
46412
|
+
function readUtf8(p) {
|
|
46413
|
+
return readFileSync3(p, "utf-8");
|
|
46506
46414
|
}
|
|
46507
|
-
function
|
|
46508
|
-
|
|
46509
|
-
if (match === void 0) {
|
|
46510
|
-
throw new Error(`${flag} must be ${choices.join(" or ")} (got "${raw2}")`);
|
|
46511
|
-
}
|
|
46512
|
-
return match;
|
|
46415
|
+
function collect(value, previous = []) {
|
|
46416
|
+
return [...previous, value];
|
|
46513
46417
|
}
|
|
46514
|
-
|
|
46515
|
-
|
|
46516
|
-
|
|
46517
|
-
const fields = {};
|
|
46518
|
-
if (opts.interval !== void 0) {
|
|
46519
|
-
fields.interval_seconds = parseDurationSeconds(opts.interval, "--interval");
|
|
46418
|
+
function parseScheduleFlags(opts) {
|
|
46419
|
+
if (opts.every !== void 0 && opts.cron !== void 0) {
|
|
46420
|
+
throw new Error("give --every or --cron, not both");
|
|
46520
46421
|
}
|
|
46521
|
-
|
|
46522
|
-
if (opts.
|
|
46523
|
-
if (opts.
|
|
46524
|
-
|
|
46422
|
+
const fields = {};
|
|
46423
|
+
if (opts.artifact !== void 0) fields.artifact_name = opts.artifact;
|
|
46424
|
+
if (opts.flavor !== void 0) fields.runtime_flavor = opts.flavor;
|
|
46425
|
+
if (opts.datasource !== void 0) fields.datasources = parseDatasources(opts.datasource);
|
|
46426
|
+
if (opts.primaryRole !== void 0) fields.primary_role = opts.primaryRole;
|
|
46427
|
+
if (opts.config !== void 0) fields.config = parseConfig(opts.config);
|
|
46428
|
+
if (opts.every !== void 0) fields.every_seconds = parseEvery(opts.every);
|
|
46429
|
+
if (opts.cron !== void 0) fields.cron = parseCron(opts.cron);
|
|
46430
|
+
if (opts.tz !== void 0) fields.timezone = opts.tz;
|
|
46431
|
+
if (fields.primary_role !== void 0 && fields.datasources !== void 0) {
|
|
46432
|
+
if (!Object.hasOwn(fields.datasources, fields.primary_role)) {
|
|
46433
|
+
throw new Error(
|
|
46434
|
+
`--primary-role "${fields.primary_role}" is not one of the --datasource roles`
|
|
46435
|
+
);
|
|
46436
|
+
}
|
|
46525
46437
|
}
|
|
46526
|
-
if (opts.sameName !== void 0) fields.change_policy = parseSameName(opts.sameName);
|
|
46527
46438
|
return fields;
|
|
46528
46439
|
}
|
|
46529
|
-
function
|
|
46530
|
-
const
|
|
46531
|
-
["--
|
|
46532
|
-
["--
|
|
46533
|
-
["--
|
|
46534
|
-
["--
|
|
46535
|
-
|
|
46536
|
-
|
|
46537
|
-
|
|
46440
|
+
function buildCreateFields(name, orgId, opts) {
|
|
46441
|
+
const missing = [
|
|
46442
|
+
["--artifact", opts.artifact],
|
|
46443
|
+
["--flavor", opts.flavor],
|
|
46444
|
+
["--datasource", opts.datasource],
|
|
46445
|
+
["--config", opts.config]
|
|
46446
|
+
].filter(([, v]) => v === void 0).map(([flag]) => flag);
|
|
46447
|
+
if (opts.every === void 0 && opts.cron === void 0) missing.push("--every or --cron");
|
|
46448
|
+
if (missing.length > 0) throw new Error(`missing ${missing.join(", ")}`);
|
|
46449
|
+
const fields = parseScheduleFlags(opts);
|
|
46450
|
+
return {
|
|
46451
|
+
org_id: orgId,
|
|
46452
|
+
name,
|
|
46453
|
+
artifact_name: fields.artifact_name,
|
|
46454
|
+
runtime_flavor: fields.runtime_flavor,
|
|
46455
|
+
datasources: fields.datasources,
|
|
46456
|
+
config: fields.config,
|
|
46457
|
+
...fields.primary_role !== void 0 ? { primary_role: fields.primary_role } : {},
|
|
46458
|
+
...fields.every_seconds !== void 0 ? { every_seconds: fields.every_seconds } : {},
|
|
46459
|
+
...fields.cron !== void 0 ? { cron: fields.cron } : {},
|
|
46460
|
+
...fields.timezone !== void 0 ? { timezone: fields.timezone } : {},
|
|
46461
|
+
...opts.disabled === true ? { enabled: false } : {}
|
|
46462
|
+
};
|
|
46538
46463
|
}
|
|
46539
|
-
|
|
46540
|
-
|
|
46541
|
-
|
|
46542
|
-
const
|
|
46543
|
-
const
|
|
46544
|
-
|
|
46545
|
-
);
|
|
46546
|
-
const [first] = matches;
|
|
46547
|
-
if (first === void 0) {
|
|
46464
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
46465
|
+
async function resolveTaskId(client, task, signal) {
|
|
46466
|
+
if (UUID_RE.test(task)) return task;
|
|
46467
|
+
const tasks = await listTasks(client, void 0, { signal });
|
|
46468
|
+
const match = tasks.find((t) => t.name === task);
|
|
46469
|
+
if (match === void 0) {
|
|
46548
46470
|
throw new Error(
|
|
46549
|
-
`no
|
|
46471
|
+
`no scheduled task named "${task}" in this org (see: dr connectors tasks list)`
|
|
46550
46472
|
);
|
|
46551
46473
|
}
|
|
46552
|
-
|
|
46553
|
-
const ids = matches.map((q) => `#${q.query_id}`).join(", ");
|
|
46554
|
-
throw new Error(`filebox ${wanted} has several connector bindings (${ids})`);
|
|
46555
|
-
}
|
|
46556
|
-
return first;
|
|
46474
|
+
return match.id;
|
|
46557
46475
|
}
|
|
46558
|
-
function
|
|
46559
|
-
|
|
46476
|
+
function explainTaskError(err) {
|
|
46477
|
+
const forbidden = err instanceof ControlPlaneError && err.status === 403 || err instanceof Error && /\bHTTP 403\b/.test(err.message);
|
|
46478
|
+
if (forbidden) {
|
|
46479
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
46480
|
+
const body = err instanceof ControlPlaneError ? err.body : (/HTTP 403[^:]*(?:\([^)]*\))?:\s*([\s\S]*)$/.exec(message)?.[1] ?? "").trim();
|
|
46481
|
+
return withCause(
|
|
46482
|
+
new ControlPlaneError(
|
|
46483
|
+
403,
|
|
46484
|
+
body,
|
|
46485
|
+
`${message}
|
|
46486
|
+
Scheduled tasks need a support user or an org admin: log in as one (--account) \u2014 e.g. dr login-as the customer's support user.`
|
|
46487
|
+
),
|
|
46488
|
+
err
|
|
46489
|
+
);
|
|
46490
|
+
}
|
|
46491
|
+
if (!(err instanceof ControlPlaneError)) return err;
|
|
46492
|
+
const detail = detailOf(err.body);
|
|
46493
|
+
if (err.status === 404) {
|
|
46494
|
+
const message = `scheduled task not found (${detail || err.message})`;
|
|
46495
|
+
return withCause(new ControlPlaneError(404, err.body, message), err);
|
|
46496
|
+
}
|
|
46497
|
+
if (err.status === 409 || err.status === 422) {
|
|
46498
|
+
return withCause(new ControlPlaneError(err.status, err.body, detail || err.message), err);
|
|
46499
|
+
}
|
|
46500
|
+
return err;
|
|
46560
46501
|
}
|
|
46561
|
-
function
|
|
46562
|
-
|
|
46502
|
+
function withCause(error, cause) {
|
|
46503
|
+
Object.defineProperty(error, "cause", { value: cause, enumerable: false, configurable: true });
|
|
46504
|
+
return error;
|
|
46563
46505
|
}
|
|
46564
|
-
|
|
46506
|
+
function detailOf(body) {
|
|
46565
46507
|
try {
|
|
46566
|
-
|
|
46567
|
-
|
|
46568
|
-
if (
|
|
46569
|
-
|
|
46570
|
-
|
|
46571
|
-
{
|
|
46572
|
-
);
|
|
46508
|
+
const parsed2 = JSON.parse(body);
|
|
46509
|
+
const d = parsed2.detail;
|
|
46510
|
+
if (typeof d === "string") return d;
|
|
46511
|
+
if (Array.isArray(d)) {
|
|
46512
|
+
return d.map(
|
|
46513
|
+
(e) => `${Array.isArray(e.loc) ? e.loc.slice(1).join(".") : ""}: ${e.msg ?? ""}`.trim()
|
|
46514
|
+
).join("; ");
|
|
46573
46515
|
}
|
|
46574
|
-
|
|
46516
|
+
} catch {
|
|
46575
46517
|
}
|
|
46518
|
+
return body;
|
|
46576
46519
|
}
|
|
46577
|
-
|
|
46578
|
-
|
|
46579
|
-
const
|
|
46580
|
-
const
|
|
46581
|
-
|
|
46582
|
-
|
|
46583
|
-
|
|
46584
|
-
|
|
46585
|
-
|
|
46520
|
+
function scheduleText(t) {
|
|
46521
|
+
if (t.schedule_kind === "cron") return `cron "${t.cron ?? ""}" ${t.timezone}`;
|
|
46522
|
+
const s = t.every_seconds ?? 0;
|
|
46523
|
+
const unit = s % 86400 === 0 ? `${s / 86400}d` : s % 3600 === 0 ? `${s / 3600}h` : s % 60 === 0 ? `${s / 60}m` : `${s}s`;
|
|
46524
|
+
return `every ${unit}`;
|
|
46525
|
+
}
|
|
46526
|
+
function stateText(t) {
|
|
46527
|
+
if (!t.enabled) return "disabled";
|
|
46528
|
+
if (t.paused_reason !== null) return `paused (${t.paused_reason})`;
|
|
46529
|
+
return t.running ? "running" : "enabled";
|
|
46530
|
+
}
|
|
46531
|
+
function writeTask(out, t) {
|
|
46532
|
+
const roles = Object.entries(t.datasources).map(([role, id]) => `${role}=${id}${role === t.primary_role ? " (primary)" : ""}`).join(", ");
|
|
46533
|
+
out.write(`Task: ${t.name} (${t.id})
|
|
46534
|
+
`);
|
|
46535
|
+
out.write(` Org: ${t.org_id}
|
|
46536
|
+
`);
|
|
46537
|
+
out.write(` State: ${stateText(t)}
|
|
46538
|
+
`);
|
|
46539
|
+
out.write(` Artifact: ${t.artifact_name} [${t.runtime_flavor}]
|
|
46540
|
+
`);
|
|
46541
|
+
out.write(` Schedule: ${scheduleText(t)}
|
|
46542
|
+
`);
|
|
46543
|
+
out.write(` Next run: ${t.next_run_at}
|
|
46544
|
+
`);
|
|
46545
|
+
out.write(` Last run: ${t.last_run_at ?? "-"} ${t.last_run_status ?? ""}
|
|
46546
|
+
`);
|
|
46547
|
+
if (t.consecutive_failures > 0) out.write(` Failures: ${t.consecutive_failures} in a row
|
|
46548
|
+
`);
|
|
46549
|
+
out.write(` Datasources: ${roles}
|
|
46550
|
+
`);
|
|
46551
|
+
out.write(` Config: ${truncate3(JSON.stringify(t.config), 200)}
|
|
46552
|
+
`);
|
|
46553
|
+
}
|
|
46554
|
+
function writeTaskList(out, tasks) {
|
|
46555
|
+
if (tasks.length === 0) {
|
|
46556
|
+
out.write("No scheduled tasks.\n");
|
|
46557
|
+
return;
|
|
46558
|
+
}
|
|
46559
|
+
for (const t of tasks) {
|
|
46560
|
+
out.write(
|
|
46561
|
+
`${t.name.padEnd(28)} ${stateText(t).padEnd(20)} ${scheduleText(t).padEnd(26)} next ${t.next_run_at} last ${t.last_run_status ?? "-"} ${t.id}
|
|
46562
|
+
`
|
|
46586
46563
|
);
|
|
46587
46564
|
}
|
|
46588
|
-
return { id: only.id, name: only.name };
|
|
46589
46565
|
}
|
|
46590
|
-
function
|
|
46591
|
-
if (
|
|
46592
|
-
|
|
46593
|
-
|
|
46594
|
-
|
|
46566
|
+
function writeRuns(out, runs) {
|
|
46567
|
+
if (runs.length === 0) {
|
|
46568
|
+
out.write("No runs yet.\n");
|
|
46569
|
+
return;
|
|
46570
|
+
}
|
|
46571
|
+
for (const r of runs) {
|
|
46572
|
+
const tail = r.error !== null ? ` error: ${truncate3(r.error, 160)}` : r.summary !== null ? ` ${truncate3(JSON.stringify(r.summary), 160)}` : "";
|
|
46573
|
+
out.write(
|
|
46574
|
+
`${r.started_at} ${r.status.padEnd(15)} ${r.trigger.padEnd(8)} attempt ${r.attempt} ${r.artifact_version ?? "-"}${tail}
|
|
46575
|
+
`
|
|
46576
|
+
);
|
|
46577
|
+
}
|
|
46595
46578
|
}
|
|
46596
|
-
function
|
|
46597
|
-
|
|
46579
|
+
function withFlags(cmd) {
|
|
46580
|
+
cmd.option("--json", "Output raw JSON instead of a summary", false);
|
|
46581
|
+
addServerFlag(cmd);
|
|
46582
|
+
addAccountFlag(cmd);
|
|
46583
|
+
addConnectorsUrlFlag(cmd);
|
|
46584
|
+
return cmd;
|
|
46598
46585
|
}
|
|
46599
|
-
function
|
|
46600
|
-
return
|
|
46586
|
+
function addScheduleFlags(cmd) {
|
|
46587
|
+
return cmd.option("--artifact <name>", "published artifact name, e.g. sftp_export__daily_sales").option("--flavor <flavor>", "runner flavor the artifact was published for, e.g. sftp").option("--datasource <role=id>", "monolith datasource for a role (repeat per role)", collect).option(
|
|
46588
|
+
"--primary-role <role>",
|
|
46589
|
+
"the role passed as the primary datasource (default: the first)"
|
|
46590
|
+
).option("--config <json|@file>", "task config: a JSON object, or @path to a JSON file").option("--every <duration>", "run on an interval, e.g. 5m, 1h (at least 1m)").option("--cron <expr>", 'run on a five-field cron line, e.g. "15 * * * *"').option("--tz <iana>", "timezone the cron line is read in (default UTC)");
|
|
46601
46591
|
}
|
|
46602
|
-
function
|
|
46603
|
-
|
|
46604
|
-
|
|
46592
|
+
async function onTask(task, opts, act, print) {
|
|
46593
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
46594
|
+
try {
|
|
46595
|
+
const taskId = await resolveTaskId(client, task, signal);
|
|
46596
|
+
const result = await act(client, taskId, signal);
|
|
46597
|
+
if (opts.json === true) writeJSONIndented(process.stdout, result);
|
|
46598
|
+
else print(process.stdout, result);
|
|
46599
|
+
} catch (err) {
|
|
46600
|
+
throw explainTaskError(err);
|
|
46601
|
+
}
|
|
46602
|
+
});
|
|
46605
46603
|
}
|
|
46606
|
-
function
|
|
46607
|
-
|
|
46604
|
+
function registerConnectorsTasks(connectors) {
|
|
46605
|
+
const tasks = connectors.command("tasks").description("DRIP scheduled tasks: run an artifact on a schedule (support user or org admin)");
|
|
46606
|
+
withFlags(
|
|
46607
|
+
addScheduleFlags(
|
|
46608
|
+
tasks.command("create").description("Create a scheduled task").argument("<name>", "task name, unique in the org (a-z, 0-9, _ and -)").option("--org-id <id>", "org id (defaults to the JWT org)").option("--disabled", "create it disabled", false)
|
|
46609
|
+
)
|
|
46610
|
+
).action(
|
|
46611
|
+
async (name, opts) => {
|
|
46612
|
+
const explicitOrg = opts.orgId !== void 0 ? parsePositiveIntArg2(opts.orgId, "--org-id") : void 0;
|
|
46613
|
+
const parsed2 = buildCreateFields(name, explicitOrg ?? 0, opts);
|
|
46614
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
46615
|
+
const fields = { ...parsed2, org_id: explicitOrg ?? client.getCurrentOrgId() };
|
|
46616
|
+
try {
|
|
46617
|
+
const t = await createTask(client, fields, { signal });
|
|
46618
|
+
if (opts.json === true) writeJSONIndented(process.stdout, t);
|
|
46619
|
+
else writeTask(process.stdout, t);
|
|
46620
|
+
} catch (err) {
|
|
46621
|
+
throw explainTaskError(err);
|
|
46622
|
+
}
|
|
46623
|
+
});
|
|
46624
|
+
}
|
|
46625
|
+
);
|
|
46626
|
+
withFlags(
|
|
46627
|
+
tasks.command("list").description("List the org's scheduled tasks").option("--org-id <id>", "org id (defaults to the JWT org)")
|
|
46628
|
+
).action(async (opts) => {
|
|
46629
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
46630
|
+
const orgId = opts.orgId !== void 0 ? parsePositiveIntArg2(opts.orgId, "--org-id") : void 0;
|
|
46631
|
+
try {
|
|
46632
|
+
const list28 = await listTasks(client, orgId, { signal });
|
|
46633
|
+
if (opts.json === true) writeJSONIndented(process.stdout, list28);
|
|
46634
|
+
else writeTaskList(process.stdout, list28);
|
|
46635
|
+
} catch (err) {
|
|
46636
|
+
throw explainTaskError(err);
|
|
46637
|
+
}
|
|
46638
|
+
});
|
|
46639
|
+
});
|
|
46640
|
+
withFlags(
|
|
46641
|
+
tasks.command("get").description("Show a scheduled task").argument("<task>", "task id or name")
|
|
46642
|
+
).action(
|
|
46643
|
+
async (task, opts) => onTask(
|
|
46644
|
+
task,
|
|
46645
|
+
opts,
|
|
46646
|
+
(c, id, signal) => getTask(c, id, { signal }),
|
|
46647
|
+
(o, r) => writeTask(o, r)
|
|
46648
|
+
)
|
|
46649
|
+
);
|
|
46650
|
+
withFlags(
|
|
46651
|
+
addScheduleFlags(
|
|
46652
|
+
tasks.command("update").description("Change a scheduled task (only the flags given change)").argument("<task>", "task id or name").option("--enable", "enable the task").option("--disable", "disable the task")
|
|
46653
|
+
)
|
|
46654
|
+
).action(
|
|
46655
|
+
async (task, opts) => {
|
|
46656
|
+
if (opts.enable === true && opts.disable === true)
|
|
46657
|
+
throw new Error("give --enable or --disable, not both");
|
|
46658
|
+
const fields = parseScheduleFlags(opts);
|
|
46659
|
+
if (opts.enable === true) fields.enabled = true;
|
|
46660
|
+
if (opts.disable === true) fields.enabled = false;
|
|
46661
|
+
if (Object.keys(fields).length === 0)
|
|
46662
|
+
throw new Error("nothing to change: give at least one flag");
|
|
46663
|
+
await onTask(
|
|
46664
|
+
task,
|
|
46665
|
+
opts,
|
|
46666
|
+
(c, id, signal) => updateTask(c, id, fields, { signal }),
|
|
46667
|
+
(o, r) => writeTask(o, r)
|
|
46668
|
+
);
|
|
46669
|
+
}
|
|
46670
|
+
);
|
|
46671
|
+
withFlags(
|
|
46672
|
+
tasks.command("delete").description("Delete a scheduled task and its run history").argument("<task>", "task id or name")
|
|
46673
|
+
).action(
|
|
46674
|
+
async (task, opts) => onTask(
|
|
46675
|
+
task,
|
|
46676
|
+
opts,
|
|
46677
|
+
async (c, id, signal) => {
|
|
46678
|
+
await deleteTask(c, id, { signal });
|
|
46679
|
+
return { deleted: id };
|
|
46680
|
+
},
|
|
46681
|
+
(o, r) => o.write(`Deleted scheduled task ${r.deleted}
|
|
46682
|
+
`)
|
|
46683
|
+
)
|
|
46684
|
+
);
|
|
46685
|
+
withFlags(
|
|
46686
|
+
tasks.command("run").description("Run a task now (does not move its schedule)").argument("<task>", "task id or name")
|
|
46687
|
+
).action(
|
|
46688
|
+
async (task, opts) => onTask(
|
|
46689
|
+
task,
|
|
46690
|
+
opts,
|
|
46691
|
+
(c, id, signal) => runTask(c, id, { signal }),
|
|
46692
|
+
(o, r) => {
|
|
46693
|
+
const run2 = r;
|
|
46694
|
+
o.write(`Queued run ${run2.id} (${run2.status}); see: dr connectors tasks runs ${task}
|
|
46695
|
+
`);
|
|
46696
|
+
}
|
|
46697
|
+
)
|
|
46698
|
+
);
|
|
46699
|
+
withFlags(
|
|
46700
|
+
tasks.command("pause").description("Pause a scheduled task").argument("<task>", "task id or name")
|
|
46701
|
+
).action(
|
|
46702
|
+
async (task, opts) => onTask(
|
|
46703
|
+
task,
|
|
46704
|
+
opts,
|
|
46705
|
+
(c, id, signal) => pauseTask(c, id, { signal }),
|
|
46706
|
+
(o, r) => writeTask(o, r)
|
|
46707
|
+
)
|
|
46708
|
+
);
|
|
46709
|
+
withFlags(
|
|
46710
|
+
tasks.command("resume").description("Resume a paused task (clears its failure count, schedules the next run)").argument("<task>", "task id or name")
|
|
46711
|
+
).action(
|
|
46712
|
+
async (task, opts) => onTask(
|
|
46713
|
+
task,
|
|
46714
|
+
opts,
|
|
46715
|
+
(c, id, signal) => resumeTask(c, id, { signal }),
|
|
46716
|
+
(o, r) => writeTask(o, r)
|
|
46717
|
+
)
|
|
46718
|
+
);
|
|
46719
|
+
withFlags(
|
|
46720
|
+
tasks.command("runs").description("A task's run history, newest first").argument("<task>", "task id or name").option("--limit <n>", "how many runs (1-200)", "20")
|
|
46721
|
+
).action(async (task, opts) => {
|
|
46722
|
+
const limit = parsePositiveIntArg2(opts.limit, "--limit");
|
|
46723
|
+
if (limit > 200) throw new Error("invalid --limit: at most 200");
|
|
46724
|
+
await onTask(
|
|
46725
|
+
task,
|
|
46726
|
+
opts,
|
|
46727
|
+
(c, id, signal) => listRuns(c, id, limit, { signal }),
|
|
46728
|
+
(o, r) => writeRuns(o, r)
|
|
46729
|
+
);
|
|
46730
|
+
});
|
|
46608
46731
|
}
|
|
46609
|
-
|
|
46732
|
+
|
|
46733
|
+
// src/commands/connectorsAws.ts
|
|
46734
|
+
var ROLE_ARN_RE = /^arn:aws[a-z-]*:iam::\d{12}:role\/\S+$/;
|
|
46735
|
+
var OTHER_ORG_HINT = "if your session is valid, the connection or --org-id may belong to another org than the --account you are logged in as";
|
|
46736
|
+
function explainAwsError(err, id) {
|
|
46737
|
+
if (!(err instanceof ControlPlaneError)) {
|
|
46738
|
+
if (err instanceof Error && /\bHTTP 403\b/.test(err.message)) {
|
|
46739
|
+
return withCause(
|
|
46740
|
+
new ControlPlaneError(
|
|
46741
|
+
403,
|
|
46742
|
+
"",
|
|
46743
|
+
`${err.message}
|
|
46744
|
+
${OTHER_ORG_HINT}`
|
|
46745
|
+
),
|
|
46746
|
+
err
|
|
46747
|
+
);
|
|
46748
|
+
}
|
|
46749
|
+
return err;
|
|
46750
|
+
}
|
|
46751
|
+
if (!hasDetail(err.body)) return err;
|
|
46752
|
+
const detail = detailOf(err.body) || err.message;
|
|
46753
|
+
const messages = {
|
|
46754
|
+
403: `forbidden: ${detail} (${OTHER_ORG_HINT})`,
|
|
46755
|
+
404: `AWS connection${id === void 0 ? "" : ` ${id}`} not found: ${detail}`,
|
|
46756
|
+
409: `conflict: ${detail}`,
|
|
46757
|
+
422: `rejected: ${detail}`,
|
|
46758
|
+
503: `the control plane cannot broker AWS roles on this site: ${detail}`
|
|
46759
|
+
};
|
|
46760
|
+
const message = messages[err.status];
|
|
46761
|
+
if (message === void 0) return err;
|
|
46762
|
+
return withCause(new ControlPlaneError(err.status, err.body, message), err);
|
|
46763
|
+
}
|
|
46764
|
+
function hasDetail(body) {
|
|
46765
|
+
try {
|
|
46766
|
+
const parsed2 = JSON.parse(body);
|
|
46767
|
+
return typeof parsed2 === "object" && parsed2 !== null && "detail" in parsed2;
|
|
46768
|
+
} catch {
|
|
46769
|
+
return false;
|
|
46770
|
+
}
|
|
46771
|
+
}
|
|
46772
|
+
function oneLine3(s) {
|
|
46773
|
+
return s.replace(/\s+/g, " ").trim();
|
|
46774
|
+
}
|
|
46775
|
+
function writeConnection(out, c) {
|
|
46610
46776
|
const row = (label, value) => {
|
|
46611
|
-
out.write(` ${`${label}:`.padEnd(
|
|
46777
|
+
out.write(` ${`${label}:`.padEnd(14)}${value}
|
|
46612
46778
|
`);
|
|
46613
46779
|
};
|
|
46614
|
-
|
|
46615
|
-
|
|
46616
|
-
|
|
46780
|
+
out.write(`AWS connection #${c.id} (org ${c.org_id})
|
|
46781
|
+
`);
|
|
46782
|
+
row("Datasource", c.secret_ref);
|
|
46783
|
+
row("Role ARN", c.role_arn);
|
|
46784
|
+
row("Status", `${c.status}${c.enabled ? "" : " (disabled)"}`);
|
|
46785
|
+
row("Broker ARN", c.broker_arn ?? "-");
|
|
46786
|
+
row("External ID", c.external_id);
|
|
46787
|
+
row("Expires", c.expires_at ?? "-");
|
|
46788
|
+
row("Last assumed", c.last_assumed_at ?? "-");
|
|
46789
|
+
row("Last error", c.last_error === null ? "-" : oneLine3(c.last_error));
|
|
46790
|
+
row("Failures", String(c.consecutive_failures));
|
|
46791
|
+
if (c.trust_statement === null) {
|
|
46792
|
+
out.write("\nTrust statement: none returned by the control plane.\n");
|
|
46793
|
+
} else {
|
|
46794
|
+
out.write("\nTrust statement (add it to the customer role's trust policy):\n");
|
|
46795
|
+
out.write(`${JSON.stringify(c.trust_statement, null, 2)}
|
|
46617
46796
|
`);
|
|
46618
|
-
|
|
46619
|
-
|
|
46620
|
-
|
|
46621
|
-
row("Since", p.since ?? "-");
|
|
46622
|
-
row("Lookback", `${p.lookback_days}d`);
|
|
46623
|
-
row("Settle", formatSeconds(p.settle_seconds));
|
|
46624
|
-
row("Same name", sameNameLabel(p.change_policy));
|
|
46625
|
-
row("Max attempts", String(p.max_attempts));
|
|
46626
|
-
row("Last poll", `${p.last_poll_at ?? "-"} ${oneLine3(p.last_poll_status ?? "")}`.trimEnd());
|
|
46627
|
-
row("Next poll", p.next_poll_at);
|
|
46628
|
-
row("Failures", String(p.consecutive_failures));
|
|
46629
|
-
row("Discover", p.discover_in_flight ? `in flight since ${p.discover_sent_at ?? "?"}` : "-");
|
|
46630
|
-
row("Last digest", p.last_digest ?? "-");
|
|
46631
|
-
row("Pending seed", p.poll_run_seed_until ?? "-");
|
|
46632
|
-
row(
|
|
46633
|
-
"Pending rekey",
|
|
46634
|
-
p.poll_run_rekey_to === null ? "-" : `to ${sameNameLabel(p.poll_run_rekey_to)}`
|
|
46797
|
+
}
|
|
46798
|
+
out.write(
|
|
46799
|
+
"\nThe customer role's permissions should be read-only on their bucket/prefix (plus kms:Decrypt for a customer-managed KMS key).\n"
|
|
46635
46800
|
);
|
|
46636
|
-
row("Items", counts === "" ? "(none)" : counts);
|
|
46637
46801
|
}
|
|
46638
|
-
function
|
|
46639
|
-
|
|
46640
|
-
|
|
46641
|
-
|
|
46642
|
-
|
|
46643
|
-
|
|
46802
|
+
function writeConnectionTable(out, list28) {
|
|
46803
|
+
if (list28.length === 0) {
|
|
46804
|
+
out.write("No AWS connections for this org.\n");
|
|
46805
|
+
return;
|
|
46806
|
+
}
|
|
46807
|
+
const rows = [["ID", "SECRET_REF", "ROLE_ARN", "STATUS", "EXPIRES_AT", "LAST_ERROR"]];
|
|
46808
|
+
for (const c of list28) {
|
|
46809
|
+
rows.push([
|
|
46810
|
+
String(c.id),
|
|
46811
|
+
oneLine3(c.secret_ref),
|
|
46812
|
+
oneLine3(c.role_arn),
|
|
46813
|
+
c.status,
|
|
46814
|
+
c.expires_at ?? "-",
|
|
46815
|
+
c.last_error === null ? "-" : truncate3(oneLine3(c.last_error), 60)
|
|
46816
|
+
]);
|
|
46644
46817
|
}
|
|
46818
|
+
const widths = rows[0].map((_, i) => Math.max(...rows.map((r) => r[i].length)));
|
|
46645
46819
|
for (const r of rows) {
|
|
46646
|
-
|
|
46647
|
-
(cell, i) =>
|
|
46820
|
+
out.write(
|
|
46821
|
+
`${r.map((cell, i) => cell.padEnd(widths[i] + 2)).join("").trimEnd()}
|
|
46822
|
+
`
|
|
46648
46823
|
);
|
|
46649
|
-
out.write(`${cells.join("").trimEnd()}
|
|
46650
|
-
`);
|
|
46651
46824
|
}
|
|
46652
46825
|
}
|
|
46653
|
-
function
|
|
46654
|
-
|
|
46655
|
-
|
|
46656
|
-
|
|
46657
|
-
|
|
46658
|
-
|
|
46659
|
-
oneLine3(it.label),
|
|
46660
|
-
it.item_date ?? "-",
|
|
46661
|
-
it.state,
|
|
46662
|
-
String(it.attempts),
|
|
46663
|
-
it.version_id === null ? "-" : String(it.version_id),
|
|
46664
|
-
error === null ? "-" : truncate3(oneLine3(error), 80)
|
|
46665
|
-
]);
|
|
46666
|
-
}
|
|
46667
|
-
writeTable26(out, rows);
|
|
46826
|
+
function withFlags2(cmd) {
|
|
46827
|
+
cmd.option("--json", "Output raw JSON instead of a summary", false);
|
|
46828
|
+
addServerFlag(cmd);
|
|
46829
|
+
addAccountFlag(cmd);
|
|
46830
|
+
addConnectorsUrlFlag(cmd);
|
|
46831
|
+
return cmd;
|
|
46668
46832
|
}
|
|
46669
|
-
async function
|
|
46670
|
-
|
|
46671
|
-
|
|
46672
|
-
|
|
46673
|
-
|
|
46674
|
-
|
|
46675
|
-
|
|
46676
|
-
|
|
46677
|
-
|
|
46678
|
-
|
|
46679
|
-
|
|
46680
|
-
|
|
46681
|
-
|
|
46833
|
+
async function run(opts, act, print, id) {
|
|
46834
|
+
await withConnectorsSetup(opts, async (setup) => {
|
|
46835
|
+
let result;
|
|
46836
|
+
try {
|
|
46837
|
+
result = await act(setup);
|
|
46838
|
+
} catch (err) {
|
|
46839
|
+
throw explainAwsError(err, id);
|
|
46840
|
+
}
|
|
46841
|
+
if (opts.json === true) writeJSONIndented(process.stdout, result);
|
|
46842
|
+
else print(process.stdout, result);
|
|
46843
|
+
});
|
|
46844
|
+
}
|
|
46845
|
+
function registerConnectorsAws(connectors) {
|
|
46846
|
+
const aws = connectors.command("aws").description("Customer IAM roles DRIP assumes to read the customer's S3 (external ID, trust)");
|
|
46847
|
+
withFlags2(
|
|
46848
|
+
aws.command("create").description("Register a customer IAM role for a datasource; prints the trust statement").requiredOption("--secret-ref <datasourceId>", "monolith datasource id the role serves").requiredOption("--role-arn <arn>", "customer role, e.g. arn:aws:iam::123456789012:role/x").option("--org-id <id>", "org id (defaults to the JWT org)")
|
|
46849
|
+
).action(async (opts) => {
|
|
46850
|
+
const secretRef = opts.secretRef.trim();
|
|
46851
|
+
if (secretRef === "" || secretRef.length > 200) {
|
|
46852
|
+
throw new Error(`invalid --secret-ref "${opts.secretRef}": expected 1-200 characters`);
|
|
46853
|
+
}
|
|
46854
|
+
if (!ROLE_ARN_RE.test(opts.roleArn)) {
|
|
46855
|
+
throw new Error(
|
|
46856
|
+
`invalid --role-arn "${opts.roleArn}": expected arn:aws:iam::<12-digit account>:role/<name>`
|
|
46857
|
+
);
|
|
46858
|
+
}
|
|
46859
|
+
const orgId = opts.orgId === void 0 ? void 0 : parsePositiveIntArg2(opts.orgId, "--org-id");
|
|
46860
|
+
const fields = {
|
|
46861
|
+
secret_ref: secretRef,
|
|
46862
|
+
role_arn: opts.roleArn,
|
|
46863
|
+
...orgId !== void 0 ? { org_id: orgId } : {}
|
|
46864
|
+
};
|
|
46865
|
+
await run(
|
|
46866
|
+
opts,
|
|
46867
|
+
({ client, signal }) => createAwsConnection(client, fields, { signal }),
|
|
46868
|
+
writeConnection
|
|
46682
46869
|
);
|
|
46683
|
-
}
|
|
46684
|
-
|
|
46685
|
-
|
|
46686
|
-
|
|
46870
|
+
});
|
|
46871
|
+
withFlags2(
|
|
46872
|
+
aws.command("list").description("List the org's AWS connections").option("--secret-ref <datasourceId>", "only the connection of this datasource")
|
|
46873
|
+
).action(async (opts) => {
|
|
46874
|
+
await run(
|
|
46875
|
+
opts,
|
|
46876
|
+
({ client, signal }) => listAwsConnections(client, opts.secretRef, { signal }),
|
|
46877
|
+
writeConnectionTable
|
|
46878
|
+
);
|
|
46879
|
+
});
|
|
46880
|
+
withFlags2(
|
|
46881
|
+
aws.command("get").description("Show an AWS connection and its trust statement").argument("<id>", "connection id")
|
|
46882
|
+
).action(async (idArg, opts) => {
|
|
46883
|
+
const id = parsePositiveIntArg2(idArg, "connection id");
|
|
46884
|
+
await run(
|
|
46885
|
+
opts,
|
|
46886
|
+
({ client, signal }) => getAwsConnection(client, id, { signal }),
|
|
46887
|
+
writeConnection,
|
|
46888
|
+
id
|
|
46889
|
+
);
|
|
46890
|
+
});
|
|
46891
|
+
for (const [verb, enabled, help] of [
|
|
46892
|
+
["enable", true, "Enable an AWS connection; also retries now (stored credentials are dropped)"],
|
|
46893
|
+
["disable", false, "Disable an AWS connection (stored credentials are dropped)"]
|
|
46894
|
+
]) {
|
|
46895
|
+
withFlags2(aws.command(verb).description(help).argument("<id>", "connection id")).action(
|
|
46896
|
+
async (idArg, opts) => {
|
|
46897
|
+
const id = parsePositiveIntArg2(idArg, "connection id");
|
|
46898
|
+
await run(
|
|
46899
|
+
opts,
|
|
46900
|
+
({ client, signal }) => updateAwsConnection(client, id, { enabled }, { signal }),
|
|
46901
|
+
(out, c) => {
|
|
46902
|
+
writeConnection(out, c);
|
|
46903
|
+
if (enabled)
|
|
46904
|
+
out.write(
|
|
46905
|
+
"\nStored credentials were dropped; the poller assumes the role again now.\n"
|
|
46906
|
+
);
|
|
46907
|
+
},
|
|
46908
|
+
id
|
|
46909
|
+
);
|
|
46910
|
+
}
|
|
46687
46911
|
);
|
|
46688
46912
|
}
|
|
46689
|
-
const dateTag = await readFileboxDateTag(cfg.monolith, binding.filebox_id, cfg.signal);
|
|
46690
|
-
const stored = await putPoll(
|
|
46691
|
-
cfg.client,
|
|
46692
|
-
binding.query_id,
|
|
46693
|
-
{ enabled: true, date_tag: dateTag, ...s },
|
|
46694
|
-
{ signal: cfg.signal }
|
|
46695
|
-
);
|
|
46696
|
-
if (cfg.asJson) {
|
|
46697
|
-
writeJSONIndented(cfg.out, stored);
|
|
46698
|
-
return;
|
|
46699
|
-
}
|
|
46700
|
-
renderPoll(cfg.out, "Polling enabled", binding, stored);
|
|
46701
|
-
}
|
|
46702
|
-
function windowStart(since, lookbackDays, now) {
|
|
46703
|
-
const start = now - lookbackDays * 864e5;
|
|
46704
|
-
return since === null ? start : Math.max(start, Date.parse(since));
|
|
46705
|
-
}
|
|
46706
|
-
function widening(current, changes, now) {
|
|
46707
|
-
if (changes.since === void 0 && changes.lookback_days === void 0) return null;
|
|
46708
|
-
const from = windowStart(current.since, current.lookback_days, now);
|
|
46709
|
-
const to = windowStart(
|
|
46710
|
-
changes.since ?? current.since,
|
|
46711
|
-
changes.lookback_days ?? current.lookback_days,
|
|
46712
|
-
now
|
|
46713
|
-
);
|
|
46714
|
-
if (to >= from) return null;
|
|
46715
|
-
return { from: new Date(from).toISOString(), to: new Date(to).toISOString() };
|
|
46716
46913
|
}
|
|
46717
|
-
|
|
46718
|
-
|
|
46719
|
-
|
|
46720
|
-
|
|
46914
|
+
|
|
46915
|
+
// src/commands/connectorsDevFetch.ts
|
|
46916
|
+
import { promises as fs19 } from "fs";
|
|
46917
|
+
import * as path9 from "path";
|
|
46918
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
46919
|
+
function buildDevEnvelope(i) {
|
|
46920
|
+
const config = {
|
|
46921
|
+
...i.publicConfig,
|
|
46922
|
+
...i.queryConfig,
|
|
46923
|
+
// Coerce absent values to null (not undefined) so JSON.stringify keeps the
|
|
46924
|
+
// key — matches build_hook_job's dq.get(...) -> None; a dropped objectType
|
|
46925
|
+
// becomes a cryptic KeyError in the runner's dispatch.
|
|
46926
|
+
objectType: i.dataQuery.objectType ?? null,
|
|
46927
|
+
type: i.dataQuery.type ?? null,
|
|
46928
|
+
query: i.dataQuery.query
|
|
46929
|
+
};
|
|
46930
|
+
if (i.period) config.period = i.period;
|
|
46931
|
+
return {
|
|
46932
|
+
correlation_id: i.correlationId,
|
|
46933
|
+
config,
|
|
46934
|
+
secret_ref: i.secretRef,
|
|
46935
|
+
cred_token: "dev-local",
|
|
46936
|
+
output_sas: "file:///out/output.csv",
|
|
46937
|
+
input_sas: null,
|
|
46938
|
+
cursor: null,
|
|
46939
|
+
contract_version: i.contractVersion,
|
|
46940
|
+
metadata: {}
|
|
46941
|
+
};
|
|
46721
46942
|
}
|
|
46722
|
-
|
|
46723
|
-
|
|
46724
|
-
|
|
46725
|
-
|
|
46943
|
+
function asObject(v) {
|
|
46944
|
+
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
46945
|
+
return v;
|
|
46946
|
+
}
|
|
46947
|
+
return null;
|
|
46726
46948
|
}
|
|
46727
|
-
|
|
46728
|
-
|
|
46949
|
+
function extractDataQueryFields(raw2) {
|
|
46950
|
+
const root = asObject(decodeShape(raw2));
|
|
46951
|
+
if (root === null || Object.keys(root).length === 0) {
|
|
46729
46952
|
throw new Error(
|
|
46730
|
-
"
|
|
46953
|
+
"no data-query attached to this filebox (empty data_query response) \u2014 attach one in the filebox UI first"
|
|
46731
46954
|
);
|
|
46732
46955
|
}
|
|
46733
|
-
const
|
|
46734
|
-
const
|
|
46735
|
-
|
|
46736
|
-
|
|
46737
|
-
)
|
|
46738
|
-
|
|
46739
|
-
|
|
46740
|
-
|
|
46741
|
-
|
|
46742
|
-
|
|
46743
|
-
|
|
46744
|
-
|
|
46956
|
+
const cfg = asObject(root.config);
|
|
46957
|
+
const src = cfg ?? root;
|
|
46958
|
+
const query = src.query;
|
|
46959
|
+
let queryObj;
|
|
46960
|
+
if (query === void 0 || query === null) {
|
|
46961
|
+
queryObj = {};
|
|
46962
|
+
} else {
|
|
46963
|
+
const asObj = asObject(query);
|
|
46964
|
+
if (asObj === null) {
|
|
46965
|
+
throw new Error(
|
|
46966
|
+
`data-query \`query\` is not an object (got ${typeof query}) \u2014 expected the Sync-query field`
|
|
46967
|
+
);
|
|
46745
46968
|
}
|
|
46746
|
-
|
|
46747
|
-
detail.push(`window: start ${wider.from} -> ${wider.to}, widen ${body.widen}`);
|
|
46748
|
-
} else if (cfg.widen !== void 0) {
|
|
46749
|
-
body.widen = cfg.widen;
|
|
46969
|
+
queryObj = asObj;
|
|
46750
46970
|
}
|
|
46751
|
-
|
|
46752
|
-
|
|
46753
|
-
|
|
46754
|
-
|
|
46755
|
-
|
|
46756
|
-
|
|
46757
|
-
|
|
46758
|
-
|
|
46759
|
-
|
|
46760
|
-
|
|
46971
|
+
return { objectType: queryObj.objectType, type: src.type, query: queryObj };
|
|
46972
|
+
}
|
|
46973
|
+
function extractCredentials(integration) {
|
|
46974
|
+
const cfg = asObject(integration.config);
|
|
46975
|
+
if (cfg === null || Object.keys(cfg).length === 0) {
|
|
46976
|
+
throw new Error(
|
|
46977
|
+
`datasource ${integration.id} has an empty/missing \`config\` \u2014 nothing to use as connector credentials`
|
|
46978
|
+
);
|
|
46979
|
+
}
|
|
46980
|
+
return { ...cfg };
|
|
46981
|
+
}
|
|
46982
|
+
var ENV_FILE = "env.json";
|
|
46983
|
+
var SECRETS_FILE = "secrets.json";
|
|
46984
|
+
var CONNECTION_FILE = "connection.py";
|
|
46985
|
+
var QUERY_FILE = "query.py";
|
|
46986
|
+
async function writeRunDir(input) {
|
|
46987
|
+
let connectionSource;
|
|
46988
|
+
try {
|
|
46989
|
+
connectionSource = await fs19.readFile(input.connectionPath, "utf-8");
|
|
46990
|
+
} catch {
|
|
46991
|
+
throw new Error(`${input.connectionPath}: connection source not found or unreadable`);
|
|
46992
|
+
}
|
|
46993
|
+
let querySource;
|
|
46994
|
+
try {
|
|
46995
|
+
querySource = await fs19.readFile(input.queryPath, "utf-8");
|
|
46996
|
+
} catch {
|
|
46997
|
+
throw new Error(`${input.queryPath}: dataquery source not found or unreadable`);
|
|
46998
|
+
}
|
|
46999
|
+
await fs19.mkdir(input.outDir, { recursive: true });
|
|
47000
|
+
await fs19.writeFile(
|
|
47001
|
+
path9.join(input.outDir, ENV_FILE),
|
|
47002
|
+
JSON.stringify(input.env, null, 2) + "\n",
|
|
47003
|
+
"utf-8"
|
|
47004
|
+
);
|
|
47005
|
+
const secretsPath = path9.join(input.outDir, SECRETS_FILE);
|
|
47006
|
+
await fs19.writeFile(
|
|
47007
|
+
secretsPath,
|
|
47008
|
+
JSON.stringify({ [input.secretRef]: input.credentials }, null, 2) + "\n",
|
|
47009
|
+
"utf-8"
|
|
47010
|
+
);
|
|
47011
|
+
if (process.platform !== "win32") {
|
|
47012
|
+
await fs19.chmod(secretsPath, 384);
|
|
47013
|
+
}
|
|
47014
|
+
await fs19.writeFile(path9.join(input.outDir, CONNECTION_FILE), connectionSource, "utf-8");
|
|
47015
|
+
await fs19.writeFile(path9.join(input.outDir, QUERY_FILE), querySource, "utf-8");
|
|
47016
|
+
}
|
|
47017
|
+
async function ensureGitignored(entry) {
|
|
47018
|
+
const gitignorePath = path9.join(process.cwd(), ".gitignore");
|
|
47019
|
+
let current = "";
|
|
47020
|
+
try {
|
|
47021
|
+
current = await fs19.readFile(gitignorePath, "utf-8");
|
|
47022
|
+
} catch {
|
|
47023
|
+
}
|
|
47024
|
+
const normalized = entry.split("\\").join("/").replace(/\/+$/, "") + "/";
|
|
47025
|
+
const lines = current.split(/\r?\n/).map((l) => l.trim());
|
|
47026
|
+
if (lines.includes(normalized) || lines.includes(entry)) return;
|
|
47027
|
+
const sep = current === "" || current.endsWith("\n") ? "" : "\n";
|
|
47028
|
+
await fs19.writeFile(gitignorePath, `${current}${sep}${normalized}
|
|
47029
|
+
`, "utf-8");
|
|
47030
|
+
}
|
|
47031
|
+
function parsePositiveInt3(raw2, label) {
|
|
47032
|
+
const n = Number(raw2);
|
|
47033
|
+
if (!Number.isInteger(n) || String(n) !== raw2.trim() || n <= 0) {
|
|
47034
|
+
throw new Error(`invalid ${label}: "${raw2}"`);
|
|
47035
|
+
}
|
|
47036
|
+
return n;
|
|
47037
|
+
}
|
|
47038
|
+
function registerDevFetch(connectors) {
|
|
47039
|
+
const cmd = connectors.command("dev-fetch").description(
|
|
47040
|
+
"Assemble a local run dir (env.json + secrets.json + connection.py + query.py) for dev-run.sh"
|
|
47041
|
+
).argument("<fileboxUuid>", "filebox UUID of the connector binding").requiredOption(
|
|
47042
|
+
"--datasource <id>",
|
|
47043
|
+
"datasource (data-integration) id whose credentials to fetch"
|
|
47044
|
+
).requiredOption(
|
|
47045
|
+
"--document-id <n>",
|
|
47046
|
+
"numeric filebox document id (no uuid->id lookup exists; required)"
|
|
47047
|
+
).requiredOption("--connection <path>", "path to the connector's connection .py source").requiredOption("--query <path>", "path to the connector's dataquery .py source").option("--from <date>", "period start (paired with --to)").option("--to <date>", "period end (paired with --from)").option("-o, --output <dir>", "output run dir (default ./dev-run/<fileboxUuid>/)").action(async (fileboxUuid, opts) => {
|
|
47048
|
+
const datasourceId = parsePositiveInt3(opts.datasource, "datasource id");
|
|
47049
|
+
const documentId = parsePositiveInt3(opts.documentId, "document-id");
|
|
47050
|
+
if (opts.from === void 0 !== (opts.to === void 0)) {
|
|
47051
|
+
throw new Error("--from and --to must be provided together");
|
|
47052
|
+
}
|
|
47053
|
+
const defaultRoot = path9.join("dev-run", fileboxUuid);
|
|
47054
|
+
const outDir = opts.output ?? defaultRoot;
|
|
47055
|
+
const gitignoreEntry = opts.output ?? "dev-run";
|
|
47056
|
+
const serverKey = resolveServer(opts.server);
|
|
47057
|
+
const host = resolveConnectorsUrl(serverKey, opts.connectorsUrl);
|
|
47058
|
+
const monolithHost = resolve(serverKey).host;
|
|
47059
|
+
const cpSetup = await setupCall(opts.server, opts.account, { host, authHost: monolithHost });
|
|
47060
|
+
try {
|
|
47061
|
+
const binding = await getBinding(cpSetup.client, fileboxUuid, {
|
|
47062
|
+
signal: cpSetup.signal
|
|
47063
|
+
});
|
|
47064
|
+
const monoSetup = await setupCall(opts.server, opts.account, {
|
|
47065
|
+
host: monolithHost,
|
|
47066
|
+
authHost: monolithHost
|
|
47067
|
+
});
|
|
47068
|
+
try {
|
|
47069
|
+
const rawDataQuery = await getDataQueryRaw(monoSetup.client, documentId, {
|
|
47070
|
+
signal: monoSetup.signal
|
|
47071
|
+
});
|
|
47072
|
+
const dataQuery = extractDataQueryFields(rawDataQuery);
|
|
47073
|
+
if (dataQuery.objectType === void 0 || dataQuery.objectType === null || dataQuery.objectType === "") {
|
|
47074
|
+
process.stderr.write(
|
|
47075
|
+
'WARNING: objectType is not set inside the Sync-query (config.query.objectType). get_query_class will receive None \u2014 fine only if the connector has a single default dataset. Add it to the data-query, e.g. config.query.objectType = "<dataset>".\n'
|
|
47076
|
+
);
|
|
47077
|
+
}
|
|
47078
|
+
const integration = await itGet(monoSetup.client, datasourceId, {
|
|
47079
|
+
signal: monoSetup.signal
|
|
47080
|
+
});
|
|
47081
|
+
const credentials = extractCredentials(integration);
|
|
47082
|
+
let period;
|
|
47083
|
+
if (opts.from !== void 0 && opts.to !== void 0) {
|
|
47084
|
+
period = { from: opts.from, to: opts.to };
|
|
47085
|
+
}
|
|
47086
|
+
const env = buildDevEnvelope({
|
|
47087
|
+
correlationId: randomUUID2(),
|
|
47088
|
+
secretRef: binding.secret_ref,
|
|
47089
|
+
publicConfig: binding.public_config,
|
|
47090
|
+
queryConfig: binding.query_config,
|
|
47091
|
+
dataQuery,
|
|
47092
|
+
...period !== void 0 ? { period } : {},
|
|
47093
|
+
// Deliberate sandbox stub: the fetch/dispatch path doesn't branch on
|
|
47094
|
+
// contract_version, so a fixed "1" is sufficient for a local run.
|
|
47095
|
+
contractVersion: "1"
|
|
47096
|
+
});
|
|
47097
|
+
await writeRunDir({
|
|
47098
|
+
outDir,
|
|
47099
|
+
env,
|
|
47100
|
+
secretRef: binding.secret_ref,
|
|
47101
|
+
credentials,
|
|
47102
|
+
connectionPath: opts.connection,
|
|
47103
|
+
queryPath: opts.query
|
|
47104
|
+
});
|
|
47105
|
+
await ensureGitignored(gitignoreEntry);
|
|
47106
|
+
process.stdout.write(`Wrote run dir: ${outDir}
|
|
47107
|
+
`);
|
|
47108
|
+
process.stdout.write(
|
|
47109
|
+
` env.json (secret_ref=${binding.secret_ref}, flavor=${binding.runtime_flavor})
|
|
47110
|
+
`
|
|
47111
|
+
);
|
|
47112
|
+
process.stdout.write(" secrets.json (credentials NOT shown)\n");
|
|
47113
|
+
process.stdout.write(" connection.py, query.py\n");
|
|
47114
|
+
} finally {
|
|
47115
|
+
monoSetup.cleanup();
|
|
47116
|
+
}
|
|
47117
|
+
} catch (err) {
|
|
47118
|
+
throw augmentTlsCertError(err);
|
|
47119
|
+
} finally {
|
|
47120
|
+
cpSetup.cleanup();
|
|
47121
|
+
}
|
|
47122
|
+
});
|
|
47123
|
+
addServerFlag(cmd);
|
|
47124
|
+
addAccountFlag(cmd);
|
|
47125
|
+
cmd.option(
|
|
47126
|
+
"--connectors-url <url>",
|
|
47127
|
+
"Override the control-plane base URL (e.g. a kubectl port-forward http://localhost:8000)"
|
|
47128
|
+
);
|
|
47129
|
+
}
|
|
47130
|
+
|
|
47131
|
+
// src/commands/connectorsPoll.ts
|
|
47132
|
+
import * as readline2 from "readline";
|
|
47133
|
+
import { createHash as createHash11 } from "crypto";
|
|
47134
|
+
|
|
47135
|
+
// src/types/connectors.ts
|
|
47136
|
+
var POLL_ITEM_STATES = [
|
|
47137
|
+
"seen",
|
|
47138
|
+
"new",
|
|
47139
|
+
"dispatched",
|
|
47140
|
+
"running",
|
|
47141
|
+
"retry_wait",
|
|
47142
|
+
"landed",
|
|
47143
|
+
"failed",
|
|
47144
|
+
"skipped",
|
|
47145
|
+
"seeded",
|
|
47146
|
+
"superseded"
|
|
47147
|
+
];
|
|
47148
|
+
|
|
47149
|
+
// src/commands/connectorsPoll.ts
|
|
47150
|
+
var DURATION_UNIT_SECONDS2 = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
47151
|
+
var DURATION_BOUNDS = {
|
|
47152
|
+
"--interval": [300, 7 * 86400],
|
|
47153
|
+
"--settle": [0, 86400]
|
|
47154
|
+
};
|
|
47155
|
+
var LOOKBACK_MAX_DAYS = 3650;
|
|
47156
|
+
function parseDurationSeconds(raw2, flag) {
|
|
47157
|
+
const m = /^(\d+)([smhd])?$/.exec(raw2.trim());
|
|
47158
|
+
const seconds = m === null ? NaN : Number(m[1]) * (DURATION_UNIT_SECONDS2[m[2] ?? "s"] ?? 1);
|
|
47159
|
+
const [min, max] = DURATION_BOUNDS[flag] ?? [0, Number.MAX_SAFE_INTEGER];
|
|
47160
|
+
if (m === null) {
|
|
47161
|
+
throw new Error(`invalid ${flag} "${raw2}": expected a duration like 90s, 5m or 1h`);
|
|
47162
|
+
}
|
|
47163
|
+
if (!Number.isSafeInteger(seconds) || seconds < min || seconds > max) {
|
|
47164
|
+
throw new Error(
|
|
47165
|
+
`invalid ${flag} "${raw2}": must be between ${formatSeconds(min)} and ${formatSeconds(max)}`
|
|
47166
|
+
);
|
|
47167
|
+
}
|
|
47168
|
+
return seconds;
|
|
47169
|
+
}
|
|
47170
|
+
function parseLookbackDays(raw2) {
|
|
47171
|
+
const m = /^(\d+)d?$/.exec(raw2.trim());
|
|
47172
|
+
const days = m === null ? 0 : Number(m[1]);
|
|
47173
|
+
if (days < 1 || days > LOOKBACK_MAX_DAYS) {
|
|
47174
|
+
throw new Error(
|
|
47175
|
+
`invalid --lookback "${raw2}": must be between 1 and ${LOOKBACK_MAX_DAYS} days, e.g. 180d`
|
|
47176
|
+
);
|
|
47177
|
+
}
|
|
47178
|
+
return days;
|
|
47179
|
+
}
|
|
47180
|
+
var DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
47181
|
+
var DATE_TIME_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-](\d{2}):(\d{2}))$/;
|
|
47182
|
+
function inRange(value, max, min = 0) {
|
|
47183
|
+
const n = Number(value ?? "0");
|
|
47184
|
+
return n >= min && n <= max;
|
|
47185
|
+
}
|
|
47186
|
+
function isCalendarDay(y, m, d) {
|
|
47187
|
+
if (!inRange(m, 12, 1)) return false;
|
|
47188
|
+
const daysInMonth3 = new Date(Date.UTC(Number(y), Number(m), 0)).getUTCDate();
|
|
47189
|
+
return inRange(d, daysInMonth3, 1);
|
|
47190
|
+
}
|
|
47191
|
+
function parseTimestampFlag(raw2, flag) {
|
|
47192
|
+
const invalid = new Error(
|
|
47193
|
+
`invalid ${flag} "${raw2}": expected YYYY-MM-DD or a timestamp with a zone (e.g. 2026-10-08T17:30:00+03:00)`
|
|
47194
|
+
);
|
|
47195
|
+
const value = raw2.trim();
|
|
47196
|
+
const dateOnly = DATE_ONLY_RE.exec(value);
|
|
47197
|
+
if (dateOnly !== null) {
|
|
47198
|
+
if (!isCalendarDay(dateOnly[1], dateOnly[2], dateOnly[3])) throw invalid;
|
|
47199
|
+
return (/* @__PURE__ */ new Date(`${value}T00:00:00Z`)).toISOString();
|
|
47200
|
+
}
|
|
47201
|
+
const dt = DATE_TIME_RE.exec(value);
|
|
47202
|
+
const valid = dt !== null && isCalendarDay(dt[1], dt[2], dt[3]) && inRange(dt[4], 23) && inRange(dt[5], 59) && inRange(dt[6], 59) && inRange(dt[7], 23) && inRange(dt[8], 59);
|
|
47203
|
+
const ms = valid ? Date.parse(value) : NaN;
|
|
47204
|
+
if (Number.isNaN(ms)) throw invalid;
|
|
47205
|
+
return new Date(ms).toISOString();
|
|
47206
|
+
}
|
|
47207
|
+
function parseSameName(raw2) {
|
|
47208
|
+
if (raw2 === "new-version") return "new_version";
|
|
47209
|
+
if (raw2 === "ignore") return "ignore";
|
|
47210
|
+
throw new Error(`--same-name must be new-version or ignore (got "${raw2}")`);
|
|
47211
|
+
}
|
|
47212
|
+
function parseChoice(raw2, flag, choices) {
|
|
47213
|
+
const match = choices.find((c) => c === raw2);
|
|
47214
|
+
if (match === void 0) {
|
|
47215
|
+
throw new Error(`${flag} must be ${choices.join(" or ")} (got "${raw2}")`);
|
|
47216
|
+
}
|
|
47217
|
+
return match;
|
|
47218
|
+
}
|
|
47219
|
+
var WIDEN_CHOICES = ["seed", "land"];
|
|
47220
|
+
var POLICY_CHOICES = ["rekey", "seed", "land"];
|
|
47221
|
+
function parseSettings(opts) {
|
|
47222
|
+
const fields = {};
|
|
47223
|
+
if (opts.interval !== void 0) {
|
|
47224
|
+
fields.interval_seconds = parseDurationSeconds(opts.interval, "--interval");
|
|
47225
|
+
}
|
|
47226
|
+
if (opts.since !== void 0) fields.since = parseTimestampFlag(opts.since, "--since");
|
|
47227
|
+
if (opts.lookback !== void 0) fields.lookback_days = parseLookbackDays(opts.lookback);
|
|
47228
|
+
if (opts.settle !== void 0) {
|
|
47229
|
+
fields.settle_seconds = parseDurationSeconds(opts.settle, "--settle");
|
|
47230
|
+
}
|
|
47231
|
+
if (opts.sameName !== void 0) fields.change_policy = parseSameName(opts.sameName);
|
|
47232
|
+
return fields;
|
|
47233
|
+
}
|
|
47234
|
+
function settingsArgs(opts) {
|
|
47235
|
+
const pairs = [
|
|
47236
|
+
["--interval", opts.interval],
|
|
47237
|
+
["--since", opts.since],
|
|
47238
|
+
["--lookback", opts.lookback],
|
|
47239
|
+
["--settle", opts.settle],
|
|
47240
|
+
["--same-name", opts.sameName]
|
|
47241
|
+
];
|
|
47242
|
+
return pairs.filter((p) => p[1] !== void 0).map(([flag, value]) => ` ${flag} ${value.trim()}`).join("");
|
|
47243
|
+
}
|
|
47244
|
+
async function resolvePollBinding(client, filebox, signal) {
|
|
47245
|
+
const wanted = filebox.trim();
|
|
47246
|
+
const byId = /^\d+$/.test(wanted);
|
|
47247
|
+
const rows = await listQueries(client, { signal });
|
|
47248
|
+
const matches = rows.filter(
|
|
47249
|
+
(q) => byId ? q.filebox_id === Number(wanted) : q.filebox_uuid.toLowerCase() === wanted.toLowerCase()
|
|
47250
|
+
);
|
|
47251
|
+
const [first] = matches;
|
|
47252
|
+
if (first === void 0) {
|
|
47253
|
+
throw new Error(
|
|
47254
|
+
`no connector binding for filebox ${wanted} in this org (see dr connectors query list)`
|
|
47255
|
+
);
|
|
47256
|
+
}
|
|
47257
|
+
if (matches.length > 1) {
|
|
47258
|
+
const ids = matches.map((q) => `#${q.query_id}`).join(", ");
|
|
47259
|
+
throw new Error(`filebox ${wanted} has several connector bindings (${ids})`);
|
|
47260
|
+
}
|
|
47261
|
+
return first;
|
|
47262
|
+
}
|
|
47263
|
+
function isNotPolled(err) {
|
|
47264
|
+
return err instanceof ControlPlaneError && err.status === 404 && err.body.includes("not polled");
|
|
47265
|
+
}
|
|
47266
|
+
function isPollThrottled(err) {
|
|
47267
|
+
return err instanceof ControlPlaneError && err.status === 409 && (err.body.includes("discover is in flight") || err.body.includes("polled less than"));
|
|
47268
|
+
}
|
|
47269
|
+
async function explainNotPolled(filebox, call3) {
|
|
47270
|
+
try {
|
|
47271
|
+
return await call3;
|
|
47272
|
+
} catch (err) {
|
|
47273
|
+
if (isNotPolled(err)) {
|
|
47274
|
+
throw new Error(
|
|
47275
|
+
`filebox ${filebox} is not set to polled ingestion; run dr connectors poll enable ${filebox}`,
|
|
47276
|
+
{ cause: err }
|
|
47277
|
+
);
|
|
47278
|
+
}
|
|
47279
|
+
throw err;
|
|
47280
|
+
}
|
|
47281
|
+
}
|
|
47282
|
+
async function readFileboxDateTag(monolith, fileboxId, signal) {
|
|
47283
|
+
const details = await getDetails(monolith, fileboxId, { signal });
|
|
47284
|
+
const dateTags = (details.tags_config ?? []).filter((t) => t.type === "DATE");
|
|
47285
|
+
const [only] = dateTags;
|
|
47286
|
+
if (only === void 0) return {};
|
|
47287
|
+
if (dateTags.length > 1) {
|
|
47288
|
+
const names = dateTags.map((t) => t.name).join(", ");
|
|
47289
|
+
throw new Error(
|
|
47290
|
+
`filebox ${fileboxId} has several DATE tags (${names}); a polled filebox needs exactly one`
|
|
47291
|
+
);
|
|
47292
|
+
}
|
|
47293
|
+
return { id: only.id, name: only.name };
|
|
47294
|
+
}
|
|
47295
|
+
function formatSeconds(sec) {
|
|
47296
|
+
if (sec > 0 && sec % 86400 === 0) return `${sec / 86400}d`;
|
|
47297
|
+
if (sec > 0 && sec % 3600 === 0) return `${sec / 3600}h`;
|
|
47298
|
+
if (sec > 0 && sec % 60 === 0) return `${sec / 60}m`;
|
|
47299
|
+
return `${sec}s`;
|
|
47300
|
+
}
|
|
47301
|
+
function oneLine4(s) {
|
|
47302
|
+
return s.replace(/\s+/g, " ").trim();
|
|
47303
|
+
}
|
|
47304
|
+
function sameNameLabel(policy) {
|
|
47305
|
+
return policy === "new_version" ? "new-version" : String(policy);
|
|
47306
|
+
}
|
|
47307
|
+
function formatDateTag(tag) {
|
|
47308
|
+
if (tag?.id === void 0) return "none (undated filebox)";
|
|
47309
|
+
return `${tag.name ?? "?"} (id ${tag.id})`;
|
|
47310
|
+
}
|
|
47311
|
+
function bindingTarget(binding) {
|
|
47312
|
+
return `filebox ${binding.filebox_uuid} (query #${binding.query_id})`;
|
|
47313
|
+
}
|
|
47314
|
+
function renderPoll(out, headline, binding, p) {
|
|
47315
|
+
const row = (label, value) => {
|
|
47316
|
+
out.write(` ${`${label}:`.padEnd(15)}${value}
|
|
47317
|
+
`);
|
|
47318
|
+
};
|
|
47319
|
+
const status2 = `${p.enabled ? "enabled" : "disabled"}${p.paused_reason === null ? "" : `, paused: ${oneLine4(p.paused_reason)}`}`;
|
|
47320
|
+
const counts = Object.entries(p.counts).sort(([a], [b]) => a.localeCompare(b)).map(([state, n]) => `${state}=${n}`).join(" ");
|
|
47321
|
+
out.write(`${headline} for ${bindingTarget(binding)}
|
|
47322
|
+
`);
|
|
47323
|
+
row("Status", status2);
|
|
47324
|
+
row("Interval", formatSeconds(p.interval_seconds));
|
|
47325
|
+
row("Date tag", formatDateTag(p.date_tag));
|
|
47326
|
+
row("Since", p.since ?? "-");
|
|
47327
|
+
row("Lookback", `${p.lookback_days}d`);
|
|
47328
|
+
row("Settle", formatSeconds(p.settle_seconds));
|
|
47329
|
+
row("Same name", sameNameLabel(p.change_policy));
|
|
47330
|
+
row("Max attempts", String(p.max_attempts));
|
|
47331
|
+
row("Last poll", `${p.last_poll_at ?? "-"} ${oneLine4(p.last_poll_status ?? "")}`.trimEnd());
|
|
47332
|
+
row("Next poll", p.next_poll_at);
|
|
47333
|
+
row("Failures", String(p.consecutive_failures));
|
|
47334
|
+
row("Discover", p.discover_in_flight ? `in flight since ${p.discover_sent_at ?? "?"}` : "-");
|
|
47335
|
+
row("Last digest", p.last_digest ?? "-");
|
|
47336
|
+
row("Pending seed", p.poll_run_seed_until ?? "-");
|
|
47337
|
+
row(
|
|
47338
|
+
"Pending rekey",
|
|
47339
|
+
p.poll_run_rekey_to === null ? "-" : `to ${sameNameLabel(p.poll_run_rekey_to)}`
|
|
47340
|
+
);
|
|
47341
|
+
row("Items", counts === "" ? "(none)" : counts);
|
|
47342
|
+
}
|
|
47343
|
+
function writeTable26(out, rows) {
|
|
47344
|
+
const widths = [];
|
|
47345
|
+
for (const r of rows) {
|
|
47346
|
+
r.forEach((cell, i) => {
|
|
47347
|
+
widths[i] = Math.max(widths[i] ?? 0, cell.length);
|
|
47348
|
+
});
|
|
47349
|
+
}
|
|
47350
|
+
for (const r of rows) {
|
|
47351
|
+
const cells = r.map(
|
|
47352
|
+
(cell, i) => i === r.length - 1 ? cell : cell.padEnd((widths[i] ?? 0) + 2)
|
|
47353
|
+
);
|
|
47354
|
+
out.write(`${cells.join("").trimEnd()}
|
|
47355
|
+
`);
|
|
47356
|
+
}
|
|
47357
|
+
}
|
|
47358
|
+
function renderItemTable(out, items) {
|
|
47359
|
+
const rows = [["ID", "LABEL", "DATE", "STATE", "ATTEMPTS", "VERSION", "ERROR"]];
|
|
47360
|
+
for (const it of items) {
|
|
47361
|
+
const error = it.last_error ?? (it.skip_reason === null ? null : `skipped: ${it.skip_reason}`);
|
|
47362
|
+
rows.push([
|
|
47363
|
+
String(it.id),
|
|
47364
|
+
oneLine4(it.label),
|
|
47365
|
+
it.item_date ?? "-",
|
|
47366
|
+
it.state,
|
|
47367
|
+
String(it.attempts),
|
|
47368
|
+
it.version_id === null ? "-" : String(it.version_id),
|
|
47369
|
+
error === null ? "-" : truncate3(oneLine4(error), 80)
|
|
47370
|
+
]);
|
|
47371
|
+
}
|
|
47372
|
+
writeTable26(out, rows);
|
|
47373
|
+
}
|
|
47374
|
+
async function runPollEnable(cfg) {
|
|
47375
|
+
const binding = await resolvePollBinding(cfg.client, cfg.filebox, cfg.signal);
|
|
47376
|
+
let existing;
|
|
47377
|
+
try {
|
|
47378
|
+
existing = await getPoll(cfg.client, binding.query_id, { signal: cfg.signal });
|
|
47379
|
+
} catch (err) {
|
|
47380
|
+
if (!isNotPolled(err)) throw err;
|
|
47381
|
+
existing = null;
|
|
47382
|
+
}
|
|
47383
|
+
const s = cfg.settings;
|
|
47384
|
+
if (existing !== null && (s.since !== void 0 || s.lookback_days !== void 0 || s.change_policy !== void 0)) {
|
|
47385
|
+
throw new Error(
|
|
47386
|
+
`filebox ${cfg.filebox} is already polled; change --since, --lookback or --same-name with dr connectors poll set ${cfg.filebox}, which asks what to do with a wider window or a policy switch`
|
|
47387
|
+
);
|
|
47388
|
+
}
|
|
47389
|
+
if (binding.filebox_id <= 0) {
|
|
47390
|
+
throw new Error(
|
|
47391
|
+
`connector-query #${binding.query_id} has no filebox id recorded, so its date tag cannot be read; record it with dr connectors query repoint ${binding.query_id} --filebox-id <id>`
|
|
47392
|
+
);
|
|
47393
|
+
}
|
|
47394
|
+
const dateTag = await readFileboxDateTag(cfg.monolith, binding.filebox_id, cfg.signal);
|
|
47395
|
+
const stored = await putPoll(
|
|
47396
|
+
cfg.client,
|
|
47397
|
+
binding.query_id,
|
|
47398
|
+
{ enabled: true, date_tag: dateTag, ...s },
|
|
47399
|
+
{ signal: cfg.signal }
|
|
47400
|
+
);
|
|
47401
|
+
if (cfg.asJson) {
|
|
47402
|
+
writeJSONIndented(cfg.out, stored);
|
|
47403
|
+
return;
|
|
47404
|
+
}
|
|
47405
|
+
renderPoll(cfg.out, "Polling enabled", binding, stored);
|
|
47406
|
+
}
|
|
47407
|
+
function windowStart(since, lookbackDays, now) {
|
|
47408
|
+
const start = now - lookbackDays * 864e5;
|
|
47409
|
+
return since === null ? start : Math.max(start, Date.parse(since));
|
|
47410
|
+
}
|
|
47411
|
+
function widening(current, changes, now) {
|
|
47412
|
+
if (changes.since === void 0 && changes.lookback_days === void 0) return null;
|
|
47413
|
+
const from = windowStart(current.since, current.lookback_days, now);
|
|
47414
|
+
const to = windowStart(
|
|
47415
|
+
changes.since ?? current.since,
|
|
47416
|
+
changes.lookback_days ?? current.lookback_days,
|
|
47417
|
+
now
|
|
47418
|
+
);
|
|
47419
|
+
if (to >= from) return null;
|
|
47420
|
+
return { from: new Date(from).toISOString(), to: new Date(to).toISOString() };
|
|
47421
|
+
}
|
|
47422
|
+
async function ask(cfg, message, choices, defaultChoice) {
|
|
47423
|
+
const answer = await cfg.prompt(message, choices, defaultChoice);
|
|
47424
|
+
if (answer === null) throw new Error("poll set cancelled; nothing was changed");
|
|
47425
|
+
return parseChoice(answer, "the answer", choices);
|
|
47426
|
+
}
|
|
47427
|
+
var WIDEN_HELP = "seed = mark the newly exposed items as already landed, land = land them all";
|
|
47428
|
+
var POLICY_HELP = "rekey = re-key the ledger from a fresh listing (nothing lands again), seed = mark everything in the window as landed, land = land everything in the window";
|
|
47429
|
+
function pollSetConfirmVerb(widen, onPolicyChange) {
|
|
47430
|
+
return widen === "land" || onPolicyChange === "land" ? "set-land" : "set-seed";
|
|
47431
|
+
}
|
|
47432
|
+
async function runPollSet(cfg) {
|
|
47433
|
+
if (Object.keys(cfg.changes).length === 0) {
|
|
47434
|
+
throw new Error(
|
|
47435
|
+
"poll set: pass at least one of --interval, --since, --lookback, --settle, --same-name"
|
|
47436
|
+
);
|
|
47437
|
+
}
|
|
47438
|
+
const binding = await resolvePollBinding(cfg.client, cfg.filebox, cfg.signal);
|
|
47439
|
+
const current = await explainNotPolled(
|
|
47440
|
+
cfg.filebox,
|
|
47441
|
+
getPoll(cfg.client, binding.query_id, { signal: cfg.signal })
|
|
47442
|
+
);
|
|
47443
|
+
const body = { ...cfg.changes };
|
|
47444
|
+
const detail = [`target: ${bindingTarget(binding)}`];
|
|
47445
|
+
const wider = widening(current, cfg.changes, cfg.now ?? Date.now());
|
|
47446
|
+
if (wider !== null) {
|
|
47447
|
+
const what = `the polling window widens (start ${wider.from} -> ${wider.to})`;
|
|
47448
|
+
if (cfg.widen === void 0 && !cfg.interactive) {
|
|
47449
|
+
throw new Error(`${what}; pass --widen seed or --widen land (${WIDEN_HELP})`);
|
|
47450
|
+
}
|
|
47451
|
+
body.widen = cfg.widen ?? await ask(cfg, `${what}. ${WIDEN_HELP}.`, WIDEN_CHOICES);
|
|
47452
|
+
detail.push(`window: start ${wider.from} -> ${wider.to}, widen ${body.widen}`);
|
|
47453
|
+
} else if (cfg.widen !== void 0) {
|
|
47454
|
+
body.widen = cfg.widen;
|
|
47455
|
+
}
|
|
47456
|
+
const newPolicy = cfg.changes.change_policy;
|
|
47457
|
+
const policySwitch = newPolicy !== void 0 && newPolicy !== current.change_policy;
|
|
47458
|
+
if (policySwitch) {
|
|
47459
|
+
const change = `${sameNameLabel(current.change_policy)} -> ${sameNameLabel(newPolicy)}`;
|
|
47460
|
+
const what = `--same-name changes ${change}`;
|
|
47461
|
+
if (cfg.onPolicyChange === void 0 && !cfg.interactive) {
|
|
47462
|
+
throw new Error(`${what}; pass --on-policy-change rekey, seed or land (${POLICY_HELP})`);
|
|
47463
|
+
}
|
|
47464
|
+
body.on_policy_change = cfg.onPolicyChange ?? await ask(cfg, `${what}. ${POLICY_HELP}.`, POLICY_CHOICES, "rekey");
|
|
47465
|
+
detail.push(`same name: ${change}, on policy change ${body.on_policy_change}`);
|
|
46761
47466
|
} else if (cfg.onPolicyChange !== void 0) {
|
|
46762
47467
|
body.on_policy_change = cfg.onPolicyChange;
|
|
46763
47468
|
}
|
|
@@ -47076,793 +47781,332 @@ async function withMonolith(opts, action) {
|
|
|
47076
47781
|
function scopeOf(opts) {
|
|
47077
47782
|
const serverKey = resolveServer(opts.server);
|
|
47078
47783
|
return {
|
|
47079
|
-
serverKey,
|
|
47080
|
-
account: normaliseAccount(opts.account),
|
|
47081
|
-
controlPlane: resolveConnectorsUrl(serverKey, opts.connectorsUrl)
|
|
47082
|
-
};
|
|
47083
|
-
}
|
|
47084
|
-
function targetArgsOf(opts) {
|
|
47085
|
-
const url = (opts.connectorsUrl ?? "").trim();
|
|
47086
|
-
return ` --server ${resolveServer(opts.server)}${url === "" ? "" : ` --connectors-url ${url}`}`;
|
|
47087
|
-
}
|
|
47088
|
-
function addCommonFlags(cmd, jsonHelp) {
|
|
47089
|
-
cmd.option("--json", jsonHelp, false);
|
|
47090
|
-
addServerFlag(cmd);
|
|
47091
|
-
addAccountFlag(cmd);
|
|
47092
|
-
addConnectorsUrlFlag(cmd);
|
|
47093
|
-
}
|
|
47094
|
-
function addConfirmTokenFlag(cmd) {
|
|
47095
|
-
return cmd.option("--confirm-token <token>", "Token from a prior plan run \u2014 required to proceed");
|
|
47096
|
-
}
|
|
47097
|
-
function addSettingsFlags(cmd) {
|
|
47098
|
-
return cmd.option("--interval <duration>", "how often to poll, 5m to 7d (default: per source)").option("--since <date>", "never pick up items modified before this date or timestamp").option("--lookback <days>", "window every poll lists, 1d to 3650d (default: per source)").option("--settle <duration>", "how long an item must stay unchanged before landing, 0s to 1d").option(
|
|
47099
|
-
"--same-name <policy>",
|
|
47100
|
-
"a changed file under the same name: new-version (lands again) or ignore"
|
|
47101
|
-
);
|
|
47102
|
-
}
|
|
47103
|
-
function ttyChoicePrompt(message, choices, defaultChoice, io = { input: process.stdin, output: process.stderr }) {
|
|
47104
|
-
return new Promise((resolvePrompt) => {
|
|
47105
|
-
const rl = readline2.createInterface({
|
|
47106
|
-
input: io.input,
|
|
47107
|
-
output: io.output,
|
|
47108
|
-
...io.terminal !== void 0 ? { terminal: io.terminal } : {}
|
|
47109
|
-
});
|
|
47110
|
-
let settled = false;
|
|
47111
|
-
const settle = (value) => {
|
|
47112
|
-
if (settled) return;
|
|
47113
|
-
settled = true;
|
|
47114
|
-
rl.close();
|
|
47115
|
-
resolvePrompt(value);
|
|
47116
|
-
};
|
|
47117
|
-
rl.on("SIGINT", () => settle(null));
|
|
47118
|
-
rl.on("close", () => settle(null));
|
|
47119
|
-
io.output.write(`${message}
|
|
47120
|
-
`);
|
|
47121
|
-
const question = defaultChoice === void 0 ? `[${choices.join("/")}]: ` : `[${choices.join("/")}] (Enter = ${defaultChoice}): `;
|
|
47122
|
-
const askOnce = () => {
|
|
47123
|
-
rl.question(question, (answer) => {
|
|
47124
|
-
const picked = answer.trim().toLowerCase();
|
|
47125
|
-
if (picked === "" && defaultChoice !== void 0) {
|
|
47126
|
-
settle(defaultChoice);
|
|
47127
|
-
} else if (choices.includes(picked)) {
|
|
47128
|
-
settle(picked);
|
|
47129
|
-
} else {
|
|
47130
|
-
io.output.write(`Answer one of: ${choices.join(", ")}
|
|
47131
|
-
`);
|
|
47132
|
-
askOnce();
|
|
47133
|
-
}
|
|
47134
|
-
});
|
|
47135
|
-
};
|
|
47136
|
-
askOnce();
|
|
47137
|
-
});
|
|
47138
|
-
}
|
|
47139
|
-
var POLL_CONFIRM_HELP = [
|
|
47140
|
-
"Confirmation:",
|
|
47141
|
-
" seed, skip and set --on-policy-change seed print a plan and a --confirm-token;",
|
|
47142
|
-
" re-run the printed command, token included, to apply it.",
|
|
47143
|
-
" No dialog opens on production, so an agent or script can run a cut-over unattended:",
|
|
47144
|
-
" dr connectors poll seed <filebox> --until <cut-over> --server <prod>",
|
|
47145
|
-
" dr connectors poll seed <filebox> --until <cut-over> --server <prod> --confirm-token <token>",
|
|
47146
|
-
" (seed --dry-run counts the already-discovered items a seed would mark.)",
|
|
47147
|
-
" A write with any answer that lands files (set --widen land, set --on-policy-change land,",
|
|
47148
|
-
" resume --source land) also asks a person to type a word in a dialog on production,",
|
|
47149
|
-
" even when the same set also seeds (--on-policy-change seed --widen land)."
|
|
47150
|
-
].join("\n");
|
|
47151
|
-
function registerConnectorsPoll(connectors) {
|
|
47152
|
-
const poll2 = connectors.command("poll").description("Polled file ingestion: the control-plane lands each new file as one version").addHelpText("after", `
|
|
47153
|
-
${POLL_CONFIRM_HELP}`);
|
|
47154
|
-
const enableCmd = addSettingsFlags(
|
|
47155
|
-
poll2.command("enable").description("Poll a filebox's binding (dated with the filebox's DATE tag)").argument("<filebox>", "filebox id or uuid")
|
|
47156
|
-
).action(async (filebox, opts) => {
|
|
47157
|
-
const settings = parseSettings(opts);
|
|
47158
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47159
|
-
await withMonolith(opts, async (monolith) => {
|
|
47160
|
-
await runPollEnable({
|
|
47161
|
-
client,
|
|
47162
|
-
monolith,
|
|
47163
|
-
filebox,
|
|
47164
|
-
settings,
|
|
47165
|
-
asJson: opts.json === true,
|
|
47166
|
-
signal,
|
|
47167
|
-
out: process.stdout
|
|
47168
|
-
});
|
|
47169
|
-
});
|
|
47170
|
-
});
|
|
47171
|
-
});
|
|
47172
|
-
addCommonFlags(enableCmd, "Output the stored binding as JSON");
|
|
47173
|
-
const setCmd = addConfirmTokenFlag(
|
|
47174
|
-
addSettingsFlags(
|
|
47175
|
-
poll2.command("set").description("Change polling settings (only the given flags change)").argument("<filebox>", "filebox id or uuid")
|
|
47176
|
-
).option(
|
|
47177
|
-
"--widen <seed|land>",
|
|
47178
|
-
"a wider window: seed (mark exposed items landed) or land them"
|
|
47179
|
-
).option("--on-policy-change <rekey|seed|land>", "a --same-name switch: rekey, seed or land").addHelpText("after", `
|
|
47180
|
-
${POLL_CONFIRM_HELP}`)
|
|
47181
|
-
).action(
|
|
47182
|
-
async (filebox, opts) => {
|
|
47183
|
-
const changes = parseSettings(opts);
|
|
47184
|
-
const widen = opts.widen === void 0 ? void 0 : parseChoice(opts.widen, "--widen", WIDEN_CHOICES);
|
|
47185
|
-
const onPolicyChange = opts.onPolicyChange === void 0 ? void 0 : parseChoice(opts.onPolicyChange, "--on-policy-change", POLICY_CHOICES);
|
|
47186
|
-
await preflightConfirmToken(
|
|
47187
|
-
pollConfirmAction(filebox, pollSetConfirmVerb(widen, onPolicyChange), scopeOf(opts), {
|
|
47188
|
-
...changes,
|
|
47189
|
-
...widen !== void 0 ? { widen } : {},
|
|
47190
|
-
...onPolicyChange !== void 0 ? { on_policy_change: onPolicyChange } : {}
|
|
47191
|
-
}),
|
|
47192
|
-
opts.confirmToken
|
|
47193
|
-
);
|
|
47194
|
-
await withConnectorsSetup(opts, async (setup) => {
|
|
47195
|
-
const deadline = {
|
|
47196
|
-
pause: () => setup.pauseTimeout(),
|
|
47197
|
-
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47198
|
-
};
|
|
47199
|
-
await runPollSet({
|
|
47200
|
-
client: setup.client,
|
|
47201
|
-
filebox,
|
|
47202
|
-
changes,
|
|
47203
|
-
settingsArgs: settingsArgs(opts),
|
|
47204
|
-
...widen !== void 0 ? { widen } : {},
|
|
47205
|
-
...onPolicyChange !== void 0 ? { onPolicyChange } : {},
|
|
47206
|
-
interactive: process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
47207
|
-
prompt: async (message, choices, defaultChoice) => {
|
|
47208
|
-
deadline.pause();
|
|
47209
|
-
try {
|
|
47210
|
-
return await ttyChoicePrompt(message, choices, defaultChoice);
|
|
47211
|
-
} finally {
|
|
47212
|
-
deadline.resume();
|
|
47213
|
-
}
|
|
47214
|
-
},
|
|
47215
|
-
deadline,
|
|
47216
|
-
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47217
|
-
scope: scopeOf(opts),
|
|
47218
|
-
targetArgs: targetArgsOf(opts),
|
|
47219
|
-
asJson: opts.json === true,
|
|
47220
|
-
signal: setup.signal,
|
|
47221
|
-
out: process.stdout
|
|
47222
|
-
});
|
|
47223
|
-
});
|
|
47224
|
-
}
|
|
47225
|
-
);
|
|
47226
|
-
addCommonFlags(setCmd, "Output the stored binding as JSON");
|
|
47227
|
-
const disableCmd = poll2.command("disable").description("Stop polling (an item already in flight still finishes)").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47228
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47229
|
-
await runPollDisable({
|
|
47230
|
-
client,
|
|
47231
|
-
filebox,
|
|
47232
|
-
asJson: opts.json === true,
|
|
47233
|
-
signal,
|
|
47234
|
-
out: process.stdout
|
|
47235
|
-
});
|
|
47236
|
-
});
|
|
47237
|
-
});
|
|
47238
|
-
addCommonFlags(disableCmd, "Output the stored binding as JSON");
|
|
47239
|
-
const getCmd = poll2.command("get").description("Show polling settings, last poll, pending seed / rekey and item counts").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47240
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47241
|
-
await runPollGet({
|
|
47242
|
-
client,
|
|
47243
|
-
filebox,
|
|
47244
|
-
asJson: opts.json === true,
|
|
47245
|
-
signal,
|
|
47246
|
-
out: process.stdout
|
|
47247
|
-
});
|
|
47248
|
-
});
|
|
47249
|
-
});
|
|
47250
|
-
addCommonFlags(getCmd, "Output the raw binding as JSON");
|
|
47251
|
-
const runCmd = poll2.command("run").description("Poll now").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47252
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47253
|
-
await runPollRun({
|
|
47254
|
-
client,
|
|
47255
|
-
filebox,
|
|
47256
|
-
asJson: opts.json === true,
|
|
47257
|
-
signal,
|
|
47258
|
-
out: process.stdout
|
|
47259
|
-
});
|
|
47260
|
-
});
|
|
47261
|
-
});
|
|
47262
|
-
addCommonFlags(runCmd, "Output the binding as JSON");
|
|
47263
|
-
const resumeCmd = addConfirmTokenFlag(
|
|
47264
|
-
poll2.command("resume").description("Resume a paused binding (after fixing credentials, or a source change)").argument("<filebox>", "filebox id or uuid").option(
|
|
47265
|
-
"--source <seed|land>",
|
|
47266
|
-
"after a source change: seed (mark present items landed) or land them"
|
|
47267
|
-
)
|
|
47268
|
-
).action(async (filebox, opts) => {
|
|
47269
|
-
const source = opts.source === void 0 ? void 0 : parseChoice(opts.source, "--source", WIDEN_CHOICES);
|
|
47270
|
-
if (source === "land") {
|
|
47271
|
-
await preflightConfirmToken(
|
|
47272
|
-
pollConfirmAction(filebox, "resume-land", scopeOf(opts)),
|
|
47273
|
-
opts.confirmToken
|
|
47274
|
-
);
|
|
47275
|
-
}
|
|
47276
|
-
await withConnectorsSetup(opts, async (setup) => {
|
|
47277
|
-
await runPollResume({
|
|
47278
|
-
client: setup.client,
|
|
47279
|
-
filebox,
|
|
47280
|
-
...source !== void 0 ? { source } : {},
|
|
47281
|
-
deadline: {
|
|
47282
|
-
pause: () => setup.pauseTimeout(),
|
|
47283
|
-
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47284
|
-
},
|
|
47285
|
-
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47286
|
-
scope: scopeOf(opts),
|
|
47287
|
-
targetArgs: targetArgsOf(opts),
|
|
47288
|
-
asJson: opts.json === true,
|
|
47289
|
-
signal: setup.signal,
|
|
47290
|
-
out: process.stdout
|
|
47291
|
-
});
|
|
47292
|
-
});
|
|
47293
|
-
});
|
|
47294
|
-
addCommonFlags(resumeCmd, "Output the binding as JSON");
|
|
47295
|
-
const itemsCmd = poll2.command("items").description("List the item ledger: label, date, state, attempts, version, error").argument("<filebox>", "filebox id or uuid").option("--state <state>", `only items in this state (${POLL_ITEM_STATES.join("|")})`).option("--limit <n>", "page size (default and maximum: the control-plane page size, 100)").option("--after <id>", "start after this item id (the next-page cursor)").action(
|
|
47296
|
-
async (filebox, opts) => {
|
|
47297
|
-
const state = opts.state === void 0 ? void 0 : POLL_ITEM_STATES.find((s) => s === opts.state);
|
|
47298
|
-
if (opts.state !== void 0 && state === void 0) {
|
|
47299
|
-
throw new Error(
|
|
47300
|
-
`invalid --state "${opts.state}": expected one of ${POLL_ITEM_STATES.join(", ")}`
|
|
47301
|
-
);
|
|
47302
|
-
}
|
|
47303
|
-
const limit = opts.limit === void 0 ? void 0 : parsePositiveIntArg2(opts.limit, "--limit");
|
|
47304
|
-
const after = opts.after === void 0 ? void 0 : parsePositiveIntArg2(opts.after, "--after");
|
|
47305
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47306
|
-
await runPollItems({
|
|
47307
|
-
client,
|
|
47308
|
-
filebox,
|
|
47309
|
-
...state !== void 0 ? { state } : {},
|
|
47310
|
-
...limit !== void 0 ? { limit } : {},
|
|
47311
|
-
...after !== void 0 ? { after } : {},
|
|
47312
|
-
asJson: opts.json === true,
|
|
47313
|
-
signal,
|
|
47314
|
-
out: process.stdout
|
|
47315
|
-
});
|
|
47316
|
-
});
|
|
47317
|
-
}
|
|
47318
|
-
);
|
|
47319
|
-
addCommonFlags(itemsCmd, "Output the raw page as JSON");
|
|
47320
|
-
const retryCmd = poll2.command("retry").description("Queue a failed, waiting or skipped item again").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)").action(async (filebox, item, opts) => {
|
|
47321
|
-
const itemId = parsePositiveIntArg2(item, "item id");
|
|
47322
|
-
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47323
|
-
await runPollRetry({
|
|
47324
|
-
client,
|
|
47325
|
-
filebox,
|
|
47326
|
-
itemId,
|
|
47327
|
-
asJson: opts.json === true,
|
|
47328
|
-
signal,
|
|
47329
|
-
out: process.stdout
|
|
47330
|
-
});
|
|
47331
|
-
});
|
|
47332
|
-
});
|
|
47333
|
-
addCommonFlags(retryCmd, "Output the item as JSON");
|
|
47334
|
-
const skipCmd = addConfirmTokenFlag(
|
|
47335
|
-
poll2.command("skip").description("Skip an item so it is never landed (two-step confirmation)").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)")
|
|
47336
|
-
).action(async (filebox, item, opts) => {
|
|
47337
|
-
const itemId = parsePositiveIntArg2(item, "item id");
|
|
47338
|
-
await preflightConfirmToken(
|
|
47339
|
-
pollItemConfirmAction(filebox, itemId, scopeOf(opts)),
|
|
47340
|
-
opts.confirmToken
|
|
47341
|
-
);
|
|
47342
|
-
await withConnectorsSetup(opts, async (setup) => {
|
|
47343
|
-
await runPollSkip({
|
|
47344
|
-
client: setup.client,
|
|
47345
|
-
filebox,
|
|
47346
|
-
itemId,
|
|
47347
|
-
deadline: {
|
|
47348
|
-
pause: () => setup.pauseTimeout(),
|
|
47349
|
-
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47350
|
-
},
|
|
47351
|
-
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47352
|
-
scope: scopeOf(opts),
|
|
47353
|
-
targetArgs: targetArgsOf(opts),
|
|
47354
|
-
asJson: opts.json === true,
|
|
47355
|
-
signal: setup.signal,
|
|
47356
|
-
out: process.stdout
|
|
47357
|
-
});
|
|
47358
|
-
});
|
|
47359
|
-
});
|
|
47360
|
-
addCommonFlags(skipCmd, "Output the item as JSON");
|
|
47361
|
-
const seedCmd = addConfirmTokenFlag(
|
|
47362
|
-
poll2.command("seed").description(
|
|
47363
|
-
"Mark items modified at or before --until as already landed (cut-over; token, no dialog)"
|
|
47364
|
-
).argument("<filebox>", "filebox id or uuid").requiredOption(
|
|
47365
|
-
"--until <timestamp>",
|
|
47366
|
-
"date or timestamp with a zone, e.g. 2026-10-08T17:30:00Z"
|
|
47367
|
-
).option(
|
|
47368
|
-
"--dry-run",
|
|
47369
|
-
"count the already-discovered items it would mark, without changing anything",
|
|
47370
|
-
false
|
|
47371
|
-
).addHelpText("after", `
|
|
47372
|
-
${POLL_CONFIRM_HELP}`)
|
|
47373
|
-
).action(
|
|
47374
|
-
async (filebox, opts) => {
|
|
47375
|
-
const until = parseTimestampFlag(opts.until, "--until");
|
|
47376
|
-
const dryRun = opts.dryRun === true;
|
|
47377
|
-
if (!dryRun) {
|
|
47378
|
-
await preflightConfirmToken(
|
|
47379
|
-
pollConfirmAction(filebox, "seed", scopeOf(opts), { until }),
|
|
47380
|
-
opts.confirmToken
|
|
47381
|
-
);
|
|
47382
|
-
}
|
|
47383
|
-
await withConnectorsSetup(opts, async (setup) => {
|
|
47384
|
-
await runPollSeed({
|
|
47385
|
-
client: setup.client,
|
|
47386
|
-
filebox,
|
|
47387
|
-
until,
|
|
47388
|
-
untilArg: opts.until.trim(),
|
|
47389
|
-
dryRun,
|
|
47390
|
-
deadline: {
|
|
47391
|
-
pause: () => setup.pauseTimeout(),
|
|
47392
|
-
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47393
|
-
},
|
|
47394
|
-
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47395
|
-
scope: scopeOf(opts),
|
|
47396
|
-
targetArgs: targetArgsOf(opts),
|
|
47397
|
-
asJson: opts.json === true,
|
|
47398
|
-
signal: setup.signal,
|
|
47399
|
-
out: process.stdout
|
|
47400
|
-
});
|
|
47401
|
-
});
|
|
47402
|
-
}
|
|
47403
|
-
);
|
|
47404
|
-
addCommonFlags(seedCmd, "Output the binding (or the dry-run count) as JSON");
|
|
47405
|
-
}
|
|
47406
|
-
|
|
47407
|
-
// src/commands/connectorsTasks.ts
|
|
47408
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
47409
|
-
|
|
47410
|
-
// src/api/scheduledTasks.ts
|
|
47411
|
-
var BASE2 = "/scheduled-tasks";
|
|
47412
|
-
async function call(method, path13, send, what) {
|
|
47413
|
-
let out;
|
|
47414
|
-
try {
|
|
47415
|
-
out = await send();
|
|
47416
|
-
} catch (err) {
|
|
47417
|
-
throw toControlPlaneError(method, path13, err);
|
|
47418
|
-
}
|
|
47419
|
-
if (out === null) throw new Error(`${method} ${path13}: empty response from control-plane ${what}`);
|
|
47420
|
-
return out;
|
|
47421
|
-
}
|
|
47422
|
-
async function createTask(client, fields, reqOptions) {
|
|
47423
|
-
return call(
|
|
47424
|
-
"POST",
|
|
47425
|
-
BASE2,
|
|
47426
|
-
() => client.postJSON(BASE2, fields, reqOptions),
|
|
47427
|
-
"task create"
|
|
47428
|
-
);
|
|
47429
|
-
}
|
|
47430
|
-
async function listTasks(client, orgId, reqOptions) {
|
|
47431
|
-
const path13 = orgId === void 0 ? BASE2 : `${BASE2}?org_id=${orgId}`;
|
|
47432
|
-
return call("GET", path13, () => client.getJSON(path13, reqOptions), "task list");
|
|
47433
|
-
}
|
|
47434
|
-
async function getTask(client, taskId, reqOptions) {
|
|
47435
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
47436
|
-
return call("GET", path13, () => client.getJSON(path13, reqOptions), "task get");
|
|
47437
|
-
}
|
|
47438
|
-
async function updateTask(client, taskId, fields, reqOptions) {
|
|
47439
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
47440
|
-
return call(
|
|
47441
|
-
"PATCH",
|
|
47442
|
-
path13,
|
|
47443
|
-
() => client.patchJSON(path13, fields, reqOptions),
|
|
47444
|
-
"task update"
|
|
47445
|
-
);
|
|
47446
|
-
}
|
|
47447
|
-
async function deleteTask(client, taskId, reqOptions) {
|
|
47448
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}`;
|
|
47449
|
-
try {
|
|
47450
|
-
await client.deleteJSON(path13, reqOptions);
|
|
47451
|
-
} catch (err) {
|
|
47452
|
-
throw toControlPlaneError("DELETE", path13, err);
|
|
47453
|
-
}
|
|
47454
|
-
}
|
|
47455
|
-
async function runTask(client, taskId, reqOptions) {
|
|
47456
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/run`;
|
|
47457
|
-
return call(
|
|
47458
|
-
"POST",
|
|
47459
|
-
path13,
|
|
47460
|
-
() => client.postJSON(path13, {}, reqOptions),
|
|
47461
|
-
"task run"
|
|
47462
|
-
);
|
|
47463
|
-
}
|
|
47464
|
-
async function pauseTask(client, taskId, reqOptions) {
|
|
47465
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/pause`;
|
|
47466
|
-
return call(
|
|
47467
|
-
"POST",
|
|
47468
|
-
path13,
|
|
47469
|
-
() => client.postJSON(path13, {}, reqOptions),
|
|
47470
|
-
"task pause"
|
|
47471
|
-
);
|
|
47472
|
-
}
|
|
47473
|
-
async function resumeTask(client, taskId, reqOptions) {
|
|
47474
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/resume`;
|
|
47475
|
-
return call(
|
|
47476
|
-
"POST",
|
|
47477
|
-
path13,
|
|
47478
|
-
() => client.postJSON(path13, {}, reqOptions),
|
|
47479
|
-
"task resume"
|
|
47480
|
-
);
|
|
47481
|
-
}
|
|
47482
|
-
async function listRuns(client, taskId, limit, reqOptions) {
|
|
47483
|
-
const path13 = `/scheduled-tasks/${encodeURIComponent(taskId)}/runs?limit=${limit}`;
|
|
47484
|
-
return call(
|
|
47485
|
-
"GET",
|
|
47486
|
-
path13,
|
|
47487
|
-
() => client.getJSON(path13, reqOptions),
|
|
47488
|
-
"task runs"
|
|
47489
|
-
);
|
|
47490
|
-
}
|
|
47491
|
-
|
|
47492
|
-
// src/commands/connectorsTasks.ts
|
|
47493
|
-
var DURATION_UNIT_SECONDS2 = { s: 1, m: 60, h: 3600, d: 86400 };
|
|
47494
|
-
var MIN_EVERY_SECONDS = 60;
|
|
47495
|
-
var MAX_EVERY_SECONDS = 2 ** 31 - 1;
|
|
47496
|
-
function parseEvery(raw2) {
|
|
47497
|
-
const m = /^(\d+)([smhd])?$/.exec(raw2.trim());
|
|
47498
|
-
const seconds = m === null ? NaN : Number(m[1]) * (DURATION_UNIT_SECONDS2[m[2] ?? "s"] ?? 1);
|
|
47499
|
-
if (m === null)
|
|
47500
|
-
throw new Error(`invalid --every "${raw2}": expected a duration like 5m, 1h or 1d`);
|
|
47501
|
-
if (!Number.isSafeInteger(seconds) || seconds < MIN_EVERY_SECONDS || seconds > MAX_EVERY_SECONDS) {
|
|
47502
|
-
throw new Error(`invalid --every "${raw2}": must be at least 1m`);
|
|
47503
|
-
}
|
|
47504
|
-
return seconds;
|
|
47505
|
-
}
|
|
47506
|
-
function parseCron(raw2) {
|
|
47507
|
-
const fields = raw2.trim().split(/\s+/);
|
|
47508
|
-
if (fields.length !== 5 || fields.some((f) => f === "")) {
|
|
47509
|
-
throw new Error(`invalid --cron "${raw2}": expected five fields, e.g. "15 * * * *"`);
|
|
47510
|
-
}
|
|
47511
|
-
return fields.join(" ");
|
|
47512
|
-
}
|
|
47513
|
-
var ROLE_RE = /^[a-z][a-z_]*$/;
|
|
47514
|
-
var DATASOURCE_ID_RE = /^[0-9]{1,18}$/;
|
|
47515
|
-
function parseDatasources(values) {
|
|
47516
|
-
const out = {};
|
|
47517
|
-
for (const raw2 of values) {
|
|
47518
|
-
const eq = raw2.indexOf("=");
|
|
47519
|
-
const role = eq < 0 ? "" : raw2.slice(0, eq).trim();
|
|
47520
|
-
const id = eq < 0 ? "" : raw2.slice(eq + 1).trim();
|
|
47521
|
-
if (!ROLE_RE.test(role) || !DATASOURCE_ID_RE.test(id)) {
|
|
47522
|
-
throw new Error(
|
|
47523
|
-
`invalid --datasource "${raw2}": expected <role>=<datasource id>, e.g. target=4242 (role: lowercase letters and _; id: digits)`
|
|
47524
|
-
);
|
|
47525
|
-
}
|
|
47526
|
-
if (Object.hasOwn(out, role)) throw new Error(`--datasource role "${role}" given twice`);
|
|
47527
|
-
out[role] = id;
|
|
47528
|
-
}
|
|
47529
|
-
const count = Object.keys(out).length;
|
|
47530
|
-
if (count < 1 || count > 4) throw new Error("give between 1 and 4 --datasource roles");
|
|
47531
|
-
return out;
|
|
47532
|
-
}
|
|
47533
|
-
function parseConfig(raw2, readFile9 = readUtf8) {
|
|
47534
|
-
const text2 = raw2.startsWith("@") ? readFile9(raw2.slice(1)) : raw2;
|
|
47535
|
-
let parsed2;
|
|
47536
|
-
try {
|
|
47537
|
-
parsed2 = JSON.parse(text2);
|
|
47538
|
-
} catch (err) {
|
|
47539
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
47540
|
-
throw new Error(`invalid --config JSON: ${msg}`);
|
|
47541
|
-
}
|
|
47542
|
-
if (typeof parsed2 !== "object" || parsed2 === null || Array.isArray(parsed2)) {
|
|
47543
|
-
throw new Error("invalid --config: expected a JSON object");
|
|
47544
|
-
}
|
|
47545
|
-
return parsed2;
|
|
47546
|
-
}
|
|
47547
|
-
function readUtf8(p) {
|
|
47548
|
-
return readFileSync3(p, "utf-8");
|
|
47549
|
-
}
|
|
47550
|
-
function collect(value, previous = []) {
|
|
47551
|
-
return [...previous, value];
|
|
47552
|
-
}
|
|
47553
|
-
function parseScheduleFlags(opts) {
|
|
47554
|
-
if (opts.every !== void 0 && opts.cron !== void 0) {
|
|
47555
|
-
throw new Error("give --every or --cron, not both");
|
|
47556
|
-
}
|
|
47557
|
-
const fields = {};
|
|
47558
|
-
if (opts.artifact !== void 0) fields.artifact_name = opts.artifact;
|
|
47559
|
-
if (opts.flavor !== void 0) fields.runtime_flavor = opts.flavor;
|
|
47560
|
-
if (opts.datasource !== void 0) fields.datasources = parseDatasources(opts.datasource);
|
|
47561
|
-
if (opts.primaryRole !== void 0) fields.primary_role = opts.primaryRole;
|
|
47562
|
-
if (opts.config !== void 0) fields.config = parseConfig(opts.config);
|
|
47563
|
-
if (opts.every !== void 0) fields.every_seconds = parseEvery(opts.every);
|
|
47564
|
-
if (opts.cron !== void 0) fields.cron = parseCron(opts.cron);
|
|
47565
|
-
if (opts.tz !== void 0) fields.timezone = opts.tz;
|
|
47566
|
-
if (fields.primary_role !== void 0 && fields.datasources !== void 0) {
|
|
47567
|
-
if (!Object.hasOwn(fields.datasources, fields.primary_role)) {
|
|
47568
|
-
throw new Error(
|
|
47569
|
-
`--primary-role "${fields.primary_role}" is not one of the --datasource roles`
|
|
47570
|
-
);
|
|
47571
|
-
}
|
|
47572
|
-
}
|
|
47573
|
-
return fields;
|
|
47574
|
-
}
|
|
47575
|
-
function buildCreateFields(name, orgId, opts) {
|
|
47576
|
-
const missing = [
|
|
47577
|
-
["--artifact", opts.artifact],
|
|
47578
|
-
["--flavor", opts.flavor],
|
|
47579
|
-
["--datasource", opts.datasource],
|
|
47580
|
-
["--config", opts.config]
|
|
47581
|
-
].filter(([, v]) => v === void 0).map(([flag]) => flag);
|
|
47582
|
-
if (opts.every === void 0 && opts.cron === void 0) missing.push("--every or --cron");
|
|
47583
|
-
if (missing.length > 0) throw new Error(`missing ${missing.join(", ")}`);
|
|
47584
|
-
const fields = parseScheduleFlags(opts);
|
|
47585
|
-
return {
|
|
47586
|
-
org_id: orgId,
|
|
47587
|
-
name,
|
|
47588
|
-
artifact_name: fields.artifact_name,
|
|
47589
|
-
runtime_flavor: fields.runtime_flavor,
|
|
47590
|
-
datasources: fields.datasources,
|
|
47591
|
-
config: fields.config,
|
|
47592
|
-
...fields.primary_role !== void 0 ? { primary_role: fields.primary_role } : {},
|
|
47593
|
-
...fields.every_seconds !== void 0 ? { every_seconds: fields.every_seconds } : {},
|
|
47594
|
-
...fields.cron !== void 0 ? { cron: fields.cron } : {},
|
|
47595
|
-
...fields.timezone !== void 0 ? { timezone: fields.timezone } : {},
|
|
47596
|
-
...opts.disabled === true ? { enabled: false } : {}
|
|
47597
|
-
};
|
|
47598
|
-
}
|
|
47599
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
47600
|
-
async function resolveTaskId(client, task, signal) {
|
|
47601
|
-
if (UUID_RE.test(task)) return task;
|
|
47602
|
-
const tasks = await listTasks(client, void 0, { signal });
|
|
47603
|
-
const match = tasks.find((t) => t.name === task);
|
|
47604
|
-
if (match === void 0) {
|
|
47605
|
-
throw new Error(
|
|
47606
|
-
`no scheduled task named "${task}" in this org (see: dr connectors tasks list)`
|
|
47607
|
-
);
|
|
47608
|
-
}
|
|
47609
|
-
return match.id;
|
|
47610
|
-
}
|
|
47611
|
-
function explainTaskError(err) {
|
|
47612
|
-
const forbidden = err instanceof ControlPlaneError && err.status === 403 || err instanceof Error && /\bHTTP 403\b/.test(err.message);
|
|
47613
|
-
if (forbidden) {
|
|
47614
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
47615
|
-
const body = err instanceof ControlPlaneError ? err.body : (/HTTP 403[^:]*(?:\([^)]*\))?:\s*([\s\S]*)$/.exec(message)?.[1] ?? "").trim();
|
|
47616
|
-
return withCause(
|
|
47617
|
-
new ControlPlaneError(
|
|
47618
|
-
403,
|
|
47619
|
-
body,
|
|
47620
|
-
`${message}
|
|
47621
|
-
Scheduled tasks need a support user or an org admin: log in as one (--account) \u2014 e.g. dr login-as the customer's support user.`
|
|
47622
|
-
),
|
|
47623
|
-
err
|
|
47624
|
-
);
|
|
47625
|
-
}
|
|
47626
|
-
if (!(err instanceof ControlPlaneError)) return err;
|
|
47627
|
-
const detail = detailOf(err.body);
|
|
47628
|
-
if (err.status === 404) {
|
|
47629
|
-
const message = `scheduled task not found (${detail || err.message})`;
|
|
47630
|
-
return withCause(new ControlPlaneError(404, err.body, message), err);
|
|
47631
|
-
}
|
|
47632
|
-
if (err.status === 409 || err.status === 422) {
|
|
47633
|
-
return withCause(new ControlPlaneError(err.status, err.body, detail || err.message), err);
|
|
47634
|
-
}
|
|
47635
|
-
return err;
|
|
47636
|
-
}
|
|
47637
|
-
function withCause(error, cause) {
|
|
47638
|
-
Object.defineProperty(error, "cause", { value: cause, enumerable: false, configurable: true });
|
|
47639
|
-
return error;
|
|
47640
|
-
}
|
|
47641
|
-
function detailOf(body) {
|
|
47642
|
-
try {
|
|
47643
|
-
const parsed2 = JSON.parse(body);
|
|
47644
|
-
const d = parsed2.detail;
|
|
47645
|
-
if (typeof d === "string") return d;
|
|
47646
|
-
if (Array.isArray(d)) {
|
|
47647
|
-
return d.map(
|
|
47648
|
-
(e) => `${Array.isArray(e.loc) ? e.loc.slice(1).join(".") : ""}: ${e.msg ?? ""}`.trim()
|
|
47649
|
-
).join("; ");
|
|
47650
|
-
}
|
|
47651
|
-
} catch {
|
|
47652
|
-
}
|
|
47653
|
-
return body;
|
|
47654
|
-
}
|
|
47655
|
-
function scheduleText(t) {
|
|
47656
|
-
if (t.schedule_kind === "cron") return `cron "${t.cron ?? ""}" ${t.timezone}`;
|
|
47657
|
-
const s = t.every_seconds ?? 0;
|
|
47658
|
-
const unit = s % 86400 === 0 ? `${s / 86400}d` : s % 3600 === 0 ? `${s / 3600}h` : s % 60 === 0 ? `${s / 60}m` : `${s}s`;
|
|
47659
|
-
return `every ${unit}`;
|
|
47660
|
-
}
|
|
47661
|
-
function stateText(t) {
|
|
47662
|
-
if (!t.enabled) return "disabled";
|
|
47663
|
-
if (t.paused_reason !== null) return `paused (${t.paused_reason})`;
|
|
47664
|
-
return t.running ? "running" : "enabled";
|
|
47665
|
-
}
|
|
47666
|
-
function writeTask(out, t) {
|
|
47667
|
-
const roles = Object.entries(t.datasources).map(([role, id]) => `${role}=${id}${role === t.primary_role ? " (primary)" : ""}`).join(", ");
|
|
47668
|
-
out.write(`Task: ${t.name} (${t.id})
|
|
47669
|
-
`);
|
|
47670
|
-
out.write(` Org: ${t.org_id}
|
|
47671
|
-
`);
|
|
47672
|
-
out.write(` State: ${stateText(t)}
|
|
47673
|
-
`);
|
|
47674
|
-
out.write(` Artifact: ${t.artifact_name} [${t.runtime_flavor}]
|
|
47675
|
-
`);
|
|
47676
|
-
out.write(` Schedule: ${scheduleText(t)}
|
|
47677
|
-
`);
|
|
47678
|
-
out.write(` Next run: ${t.next_run_at}
|
|
47679
|
-
`);
|
|
47680
|
-
out.write(` Last run: ${t.last_run_at ?? "-"} ${t.last_run_status ?? ""}
|
|
47681
|
-
`);
|
|
47682
|
-
if (t.consecutive_failures > 0) out.write(` Failures: ${t.consecutive_failures} in a row
|
|
47683
|
-
`);
|
|
47684
|
-
out.write(` Datasources: ${roles}
|
|
47685
|
-
`);
|
|
47686
|
-
out.write(` Config: ${truncate3(JSON.stringify(t.config), 200)}
|
|
47687
|
-
`);
|
|
47688
|
-
}
|
|
47689
|
-
function writeTaskList(out, tasks) {
|
|
47690
|
-
if (tasks.length === 0) {
|
|
47691
|
-
out.write("No scheduled tasks.\n");
|
|
47692
|
-
return;
|
|
47693
|
-
}
|
|
47694
|
-
for (const t of tasks) {
|
|
47695
|
-
out.write(
|
|
47696
|
-
`${t.name.padEnd(28)} ${stateText(t).padEnd(20)} ${scheduleText(t).padEnd(26)} next ${t.next_run_at} last ${t.last_run_status ?? "-"} ${t.id}
|
|
47697
|
-
`
|
|
47698
|
-
);
|
|
47699
|
-
}
|
|
47700
|
-
}
|
|
47701
|
-
function writeRuns(out, runs) {
|
|
47702
|
-
if (runs.length === 0) {
|
|
47703
|
-
out.write("No runs yet.\n");
|
|
47704
|
-
return;
|
|
47705
|
-
}
|
|
47706
|
-
for (const r of runs) {
|
|
47707
|
-
const tail = r.error !== null ? ` error: ${truncate3(r.error, 160)}` : r.summary !== null ? ` ${truncate3(JSON.stringify(r.summary), 160)}` : "";
|
|
47708
|
-
out.write(
|
|
47709
|
-
`${r.started_at} ${r.status.padEnd(15)} ${r.trigger.padEnd(8)} attempt ${r.attempt} ${r.artifact_version ?? "-"}${tail}
|
|
47710
|
-
`
|
|
47711
|
-
);
|
|
47712
|
-
}
|
|
47784
|
+
serverKey,
|
|
47785
|
+
account: normaliseAccount(opts.account),
|
|
47786
|
+
controlPlane: resolveConnectorsUrl(serverKey, opts.connectorsUrl)
|
|
47787
|
+
};
|
|
47713
47788
|
}
|
|
47714
|
-
function
|
|
47715
|
-
|
|
47789
|
+
function targetArgsOf(opts) {
|
|
47790
|
+
const url = (opts.connectorsUrl ?? "").trim();
|
|
47791
|
+
return ` --server ${resolveServer(opts.server)}${url === "" ? "" : ` --connectors-url ${url}`}`;
|
|
47792
|
+
}
|
|
47793
|
+
function addCommonFlags(cmd, jsonHelp) {
|
|
47794
|
+
cmd.option("--json", jsonHelp, false);
|
|
47716
47795
|
addServerFlag(cmd);
|
|
47717
47796
|
addAccountFlag(cmd);
|
|
47718
47797
|
addConnectorsUrlFlag(cmd);
|
|
47719
|
-
return cmd;
|
|
47720
47798
|
}
|
|
47721
|
-
function
|
|
47722
|
-
return cmd.option("--
|
|
47723
|
-
"--primary-role <role>",
|
|
47724
|
-
"the role passed as the primary datasource (default: the first)"
|
|
47725
|
-
).option("--config <json|@file>", "task config: a JSON object, or @path to a JSON file").option("--every <duration>", "run on an interval, e.g. 5m, 1h (at least 1m)").option("--cron <expr>", 'run on a five-field cron line, e.g. "15 * * * *"').option("--tz <iana>", "timezone the cron line is read in (default UTC)");
|
|
47799
|
+
function addConfirmTokenFlag(cmd) {
|
|
47800
|
+
return cmd.option("--confirm-token <token>", "Token from a prior plan run \u2014 required to proceed");
|
|
47726
47801
|
}
|
|
47727
|
-
|
|
47728
|
-
|
|
47729
|
-
|
|
47730
|
-
|
|
47731
|
-
|
|
47732
|
-
|
|
47733
|
-
|
|
47734
|
-
|
|
47735
|
-
|
|
47736
|
-
|
|
47802
|
+
function addSettingsFlags(cmd) {
|
|
47803
|
+
return cmd.option("--interval <duration>", "how often to poll, 5m to 7d (default: per source)").option("--since <date>", "never pick up items modified before this date or timestamp").option("--lookback <days>", "window every poll lists, 1d to 3650d (default: per source)").option("--settle <duration>", "how long an item must stay unchanged before landing, 0s to 1d").option(
|
|
47804
|
+
"--same-name <policy>",
|
|
47805
|
+
"a changed file under the same name: new-version (lands again) or ignore"
|
|
47806
|
+
);
|
|
47807
|
+
}
|
|
47808
|
+
function ttyChoicePrompt(message, choices, defaultChoice, io = { input: process.stdin, output: process.stderr }) {
|
|
47809
|
+
return new Promise((resolvePrompt) => {
|
|
47810
|
+
const rl = readline2.createInterface({
|
|
47811
|
+
input: io.input,
|
|
47812
|
+
output: io.output,
|
|
47813
|
+
...io.terminal !== void 0 ? { terminal: io.terminal } : {}
|
|
47814
|
+
});
|
|
47815
|
+
let settled = false;
|
|
47816
|
+
const settle = (value) => {
|
|
47817
|
+
if (settled) return;
|
|
47818
|
+
settled = true;
|
|
47819
|
+
rl.close();
|
|
47820
|
+
resolvePrompt(value);
|
|
47821
|
+
};
|
|
47822
|
+
rl.on("SIGINT", () => settle(null));
|
|
47823
|
+
rl.on("close", () => settle(null));
|
|
47824
|
+
io.output.write(`${message}
|
|
47825
|
+
`);
|
|
47826
|
+
const question = defaultChoice === void 0 ? `[${choices.join("/")}]: ` : `[${choices.join("/")}] (Enter = ${defaultChoice}): `;
|
|
47827
|
+
const askOnce = () => {
|
|
47828
|
+
rl.question(question, (answer) => {
|
|
47829
|
+
const picked = answer.trim().toLowerCase();
|
|
47830
|
+
if (picked === "" && defaultChoice !== void 0) {
|
|
47831
|
+
settle(defaultChoice);
|
|
47832
|
+
} else if (choices.includes(picked)) {
|
|
47833
|
+
settle(picked);
|
|
47834
|
+
} else {
|
|
47835
|
+
io.output.write(`Answer one of: ${choices.join(", ")}
|
|
47836
|
+
`);
|
|
47837
|
+
askOnce();
|
|
47838
|
+
}
|
|
47839
|
+
});
|
|
47840
|
+
};
|
|
47841
|
+
askOnce();
|
|
47737
47842
|
});
|
|
47738
47843
|
}
|
|
47739
|
-
|
|
47740
|
-
|
|
47741
|
-
|
|
47742
|
-
|
|
47743
|
-
|
|
47744
|
-
|
|
47844
|
+
var POLL_CONFIRM_HELP = [
|
|
47845
|
+
"Confirmation:",
|
|
47846
|
+
" seed, skip and set --on-policy-change seed print a plan and a --confirm-token;",
|
|
47847
|
+
" re-run the printed command, token included, to apply it.",
|
|
47848
|
+
" No dialog opens on production, so an agent or script can run a cut-over unattended:",
|
|
47849
|
+
" dr connectors poll seed <filebox> --until <cut-over> --server <prod>",
|
|
47850
|
+
" dr connectors poll seed <filebox> --until <cut-over> --server <prod> --confirm-token <token>",
|
|
47851
|
+
" (seed --dry-run counts the already-discovered items a seed would mark.)",
|
|
47852
|
+
" A write with any answer that lands files (set --widen land, set --on-policy-change land,",
|
|
47853
|
+
" resume --source land) also asks a person to type a word in a dialog on production,",
|
|
47854
|
+
" even when the same set also seeds (--on-policy-change seed --widen land)."
|
|
47855
|
+
].join("\n");
|
|
47856
|
+
function registerConnectorsPoll(connectors) {
|
|
47857
|
+
const poll2 = connectors.command("poll").description("Polled file ingestion: the control-plane lands each new file as one version").addHelpText("after", `
|
|
47858
|
+
${POLL_CONFIRM_HELP}`);
|
|
47859
|
+
const enableCmd = addSettingsFlags(
|
|
47860
|
+
poll2.command("enable").description("Poll a filebox's binding (dated with the filebox's DATE tag)").argument("<filebox>", "filebox id or uuid")
|
|
47861
|
+
).action(async (filebox, opts) => {
|
|
47862
|
+
const settings = parseSettings(opts);
|
|
47863
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47864
|
+
await withMonolith(opts, async (monolith) => {
|
|
47865
|
+
await runPollEnable({
|
|
47866
|
+
client,
|
|
47867
|
+
monolith,
|
|
47868
|
+
filebox,
|
|
47869
|
+
settings,
|
|
47870
|
+
asJson: opts.json === true,
|
|
47871
|
+
signal,
|
|
47872
|
+
out: process.stdout
|
|
47873
|
+
});
|
|
47874
|
+
});
|
|
47875
|
+
});
|
|
47876
|
+
});
|
|
47877
|
+
addCommonFlags(enableCmd, "Output the stored binding as JSON");
|
|
47878
|
+
const setCmd = addConfirmTokenFlag(
|
|
47879
|
+
addSettingsFlags(
|
|
47880
|
+
poll2.command("set").description("Change polling settings (only the given flags change)").argument("<filebox>", "filebox id or uuid")
|
|
47881
|
+
).option(
|
|
47882
|
+
"--widen <seed|land>",
|
|
47883
|
+
"a wider window: seed (mark exposed items landed) or land them"
|
|
47884
|
+
).option("--on-policy-change <rekey|seed|land>", "a --same-name switch: rekey, seed or land").addHelpText("after", `
|
|
47885
|
+
${POLL_CONFIRM_HELP}`)
|
|
47745
47886
|
).action(
|
|
47746
|
-
async (
|
|
47747
|
-
const
|
|
47748
|
-
const
|
|
47749
|
-
|
|
47750
|
-
|
|
47751
|
-
|
|
47752
|
-
|
|
47753
|
-
|
|
47754
|
-
|
|
47755
|
-
}
|
|
47756
|
-
|
|
47757
|
-
|
|
47887
|
+
async (filebox, opts) => {
|
|
47888
|
+
const changes = parseSettings(opts);
|
|
47889
|
+
const widen = opts.widen === void 0 ? void 0 : parseChoice(opts.widen, "--widen", WIDEN_CHOICES);
|
|
47890
|
+
const onPolicyChange = opts.onPolicyChange === void 0 ? void 0 : parseChoice(opts.onPolicyChange, "--on-policy-change", POLICY_CHOICES);
|
|
47891
|
+
await preflightConfirmToken(
|
|
47892
|
+
pollConfirmAction(filebox, pollSetConfirmVerb(widen, onPolicyChange), scopeOf(opts), {
|
|
47893
|
+
...changes,
|
|
47894
|
+
...widen !== void 0 ? { widen } : {},
|
|
47895
|
+
...onPolicyChange !== void 0 ? { on_policy_change: onPolicyChange } : {}
|
|
47896
|
+
}),
|
|
47897
|
+
opts.confirmToken
|
|
47898
|
+
);
|
|
47899
|
+
await withConnectorsSetup(opts, async (setup) => {
|
|
47900
|
+
const deadline = {
|
|
47901
|
+
pause: () => setup.pauseTimeout(),
|
|
47902
|
+
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47903
|
+
};
|
|
47904
|
+
await runPollSet({
|
|
47905
|
+
client: setup.client,
|
|
47906
|
+
filebox,
|
|
47907
|
+
changes,
|
|
47908
|
+
settingsArgs: settingsArgs(opts),
|
|
47909
|
+
...widen !== void 0 ? { widen } : {},
|
|
47910
|
+
...onPolicyChange !== void 0 ? { onPolicyChange } : {},
|
|
47911
|
+
interactive: process.stdin.isTTY === true && process.stderr.isTTY === true,
|
|
47912
|
+
prompt: async (message, choices, defaultChoice) => {
|
|
47913
|
+
deadline.pause();
|
|
47914
|
+
try {
|
|
47915
|
+
return await ttyChoicePrompt(message, choices, defaultChoice);
|
|
47916
|
+
} finally {
|
|
47917
|
+
deadline.resume();
|
|
47918
|
+
}
|
|
47919
|
+
},
|
|
47920
|
+
deadline,
|
|
47921
|
+
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47922
|
+
scope: scopeOf(opts),
|
|
47923
|
+
targetArgs: targetArgsOf(opts),
|
|
47924
|
+
asJson: opts.json === true,
|
|
47925
|
+
signal: setup.signal,
|
|
47926
|
+
out: process.stdout
|
|
47927
|
+
});
|
|
47758
47928
|
});
|
|
47759
47929
|
}
|
|
47760
47930
|
);
|
|
47761
|
-
|
|
47762
|
-
|
|
47763
|
-
).action(async (opts) => {
|
|
47931
|
+
addCommonFlags(setCmd, "Output the stored binding as JSON");
|
|
47932
|
+
const disableCmd = poll2.command("disable").description("Stop polling (an item already in flight still finishes)").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47764
47933
|
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47765
|
-
|
|
47766
|
-
|
|
47767
|
-
|
|
47768
|
-
|
|
47769
|
-
|
|
47770
|
-
|
|
47771
|
-
|
|
47772
|
-
}
|
|
47934
|
+
await runPollDisable({
|
|
47935
|
+
client,
|
|
47936
|
+
filebox,
|
|
47937
|
+
asJson: opts.json === true,
|
|
47938
|
+
signal,
|
|
47939
|
+
out: process.stdout
|
|
47940
|
+
});
|
|
47773
47941
|
});
|
|
47774
47942
|
});
|
|
47775
|
-
|
|
47776
|
-
|
|
47777
|
-
|
|
47778
|
-
|
|
47779
|
-
|
|
47780
|
-
|
|
47781
|
-
|
|
47782
|
-
|
|
47783
|
-
|
|
47784
|
-
|
|
47785
|
-
|
|
47786
|
-
|
|
47787
|
-
|
|
47943
|
+
addCommonFlags(disableCmd, "Output the stored binding as JSON");
|
|
47944
|
+
const getCmd = poll2.command("get").description("Show polling settings, last poll, pending seed / rekey and item counts").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47945
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47946
|
+
await runPollGet({
|
|
47947
|
+
client,
|
|
47948
|
+
filebox,
|
|
47949
|
+
asJson: opts.json === true,
|
|
47950
|
+
signal,
|
|
47951
|
+
out: process.stdout
|
|
47952
|
+
});
|
|
47953
|
+
});
|
|
47954
|
+
});
|
|
47955
|
+
addCommonFlags(getCmd, "Output the raw binding as JSON");
|
|
47956
|
+
const runCmd = poll2.command("run").description("Poll now").argument("<filebox>", "filebox id or uuid").action(async (filebox, opts) => {
|
|
47957
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
47958
|
+
await runPollRun({
|
|
47959
|
+
client,
|
|
47960
|
+
filebox,
|
|
47961
|
+
asJson: opts.json === true,
|
|
47962
|
+
signal,
|
|
47963
|
+
out: process.stdout
|
|
47964
|
+
});
|
|
47965
|
+
});
|
|
47966
|
+
});
|
|
47967
|
+
addCommonFlags(runCmd, "Output the binding as JSON");
|
|
47968
|
+
const resumeCmd = addConfirmTokenFlag(
|
|
47969
|
+
poll2.command("resume").description("Resume a paused binding (after fixing credentials, or a source change)").argument("<filebox>", "filebox id or uuid").option(
|
|
47970
|
+
"--source <seed|land>",
|
|
47971
|
+
"after a source change: seed (mark present items landed) or land them"
|
|
47788
47972
|
)
|
|
47789
|
-
).action(
|
|
47790
|
-
|
|
47791
|
-
|
|
47792
|
-
|
|
47793
|
-
|
|
47794
|
-
|
|
47795
|
-
if (opts.disable === true) fields.enabled = false;
|
|
47796
|
-
if (Object.keys(fields).length === 0)
|
|
47797
|
-
throw new Error("nothing to change: give at least one flag");
|
|
47798
|
-
await onTask(
|
|
47799
|
-
task,
|
|
47800
|
-
opts,
|
|
47801
|
-
(c, id, signal) => updateTask(c, id, fields, { signal }),
|
|
47802
|
-
(o, r) => writeTask(o, r)
|
|
47973
|
+
).action(async (filebox, opts) => {
|
|
47974
|
+
const source = opts.source === void 0 ? void 0 : parseChoice(opts.source, "--source", WIDEN_CHOICES);
|
|
47975
|
+
if (source === "land") {
|
|
47976
|
+
await preflightConfirmToken(
|
|
47977
|
+
pollConfirmAction(filebox, "resume-land", scopeOf(opts)),
|
|
47978
|
+
opts.confirmToken
|
|
47803
47979
|
);
|
|
47804
47980
|
}
|
|
47805
|
-
|
|
47806
|
-
|
|
47807
|
-
|
|
47808
|
-
|
|
47809
|
-
|
|
47810
|
-
|
|
47811
|
-
|
|
47812
|
-
|
|
47813
|
-
|
|
47814
|
-
|
|
47815
|
-
|
|
47816
|
-
|
|
47817
|
-
|
|
47818
|
-
|
|
47819
|
-
|
|
47820
|
-
|
|
47821
|
-
|
|
47822
|
-
)
|
|
47823
|
-
|
|
47824
|
-
|
|
47825
|
-
|
|
47826
|
-
|
|
47827
|
-
(
|
|
47828
|
-
|
|
47829
|
-
|
|
47830
|
-
|
|
47981
|
+
await withConnectorsSetup(opts, async (setup) => {
|
|
47982
|
+
await runPollResume({
|
|
47983
|
+
client: setup.client,
|
|
47984
|
+
filebox,
|
|
47985
|
+
...source !== void 0 ? { source } : {},
|
|
47986
|
+
deadline: {
|
|
47987
|
+
pause: () => setup.pauseTimeout(),
|
|
47988
|
+
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
47989
|
+
},
|
|
47990
|
+
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
47991
|
+
scope: scopeOf(opts),
|
|
47992
|
+
targetArgs: targetArgsOf(opts),
|
|
47993
|
+
asJson: opts.json === true,
|
|
47994
|
+
signal: setup.signal,
|
|
47995
|
+
out: process.stdout
|
|
47996
|
+
});
|
|
47997
|
+
});
|
|
47998
|
+
});
|
|
47999
|
+
addCommonFlags(resumeCmd, "Output the binding as JSON");
|
|
48000
|
+
const itemsCmd = poll2.command("items").description("List the item ledger: label, date, state, attempts, version, error").argument("<filebox>", "filebox id or uuid").option("--state <state>", `only items in this state (${POLL_ITEM_STATES.join("|")})`).option("--limit <n>", "page size (default and maximum: the control-plane page size, 100)").option("--after <id>", "start after this item id (the next-page cursor)").action(
|
|
48001
|
+
async (filebox, opts) => {
|
|
48002
|
+
const state = opts.state === void 0 ? void 0 : POLL_ITEM_STATES.find((s) => s === opts.state);
|
|
48003
|
+
if (opts.state !== void 0 && state === void 0) {
|
|
48004
|
+
throw new Error(
|
|
48005
|
+
`invalid --state "${opts.state}": expected one of ${POLL_ITEM_STATES.join(", ")}`
|
|
48006
|
+
);
|
|
47831
48007
|
}
|
|
47832
|
-
|
|
47833
|
-
|
|
47834
|
-
|
|
47835
|
-
|
|
47836
|
-
|
|
47837
|
-
|
|
47838
|
-
|
|
47839
|
-
|
|
47840
|
-
|
|
47841
|
-
|
|
47842
|
-
|
|
47843
|
-
|
|
47844
|
-
|
|
47845
|
-
|
|
47846
|
-
|
|
47847
|
-
async (task, opts) => onTask(
|
|
47848
|
-
task,
|
|
47849
|
-
opts,
|
|
47850
|
-
(c, id, signal) => resumeTask(c, id, { signal }),
|
|
47851
|
-
(o, r) => writeTask(o, r)
|
|
47852
|
-
)
|
|
48008
|
+
const limit = opts.limit === void 0 ? void 0 : parsePositiveIntArg2(opts.limit, "--limit");
|
|
48009
|
+
const after = opts.after === void 0 ? void 0 : parsePositiveIntArg2(opts.after, "--after");
|
|
48010
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
48011
|
+
await runPollItems({
|
|
48012
|
+
client,
|
|
48013
|
+
filebox,
|
|
48014
|
+
...state !== void 0 ? { state } : {},
|
|
48015
|
+
...limit !== void 0 ? { limit } : {},
|
|
48016
|
+
...after !== void 0 ? { after } : {},
|
|
48017
|
+
asJson: opts.json === true,
|
|
48018
|
+
signal,
|
|
48019
|
+
out: process.stdout
|
|
48020
|
+
});
|
|
48021
|
+
});
|
|
48022
|
+
}
|
|
47853
48023
|
);
|
|
47854
|
-
|
|
47855
|
-
|
|
47856
|
-
|
|
47857
|
-
|
|
47858
|
-
|
|
47859
|
-
|
|
47860
|
-
|
|
47861
|
-
|
|
47862
|
-
|
|
47863
|
-
|
|
48024
|
+
addCommonFlags(itemsCmd, "Output the raw page as JSON");
|
|
48025
|
+
const retryCmd = poll2.command("retry").description("Queue a failed, waiting or skipped item again").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)").action(async (filebox, item, opts) => {
|
|
48026
|
+
const itemId = parsePositiveIntArg2(item, "item id");
|
|
48027
|
+
await withConnectorsSetup(opts, async ({ client, signal }) => {
|
|
48028
|
+
await runPollRetry({
|
|
48029
|
+
client,
|
|
48030
|
+
filebox,
|
|
48031
|
+
itemId,
|
|
48032
|
+
asJson: opts.json === true,
|
|
48033
|
+
signal,
|
|
48034
|
+
out: process.stdout
|
|
48035
|
+
});
|
|
48036
|
+
});
|
|
48037
|
+
});
|
|
48038
|
+
addCommonFlags(retryCmd, "Output the item as JSON");
|
|
48039
|
+
const skipCmd = addConfirmTokenFlag(
|
|
48040
|
+
poll2.command("skip").description("Skip an item so it is never landed (two-step confirmation)").argument("<filebox>", "filebox id or uuid").argument("<item>", "item id (from poll items)")
|
|
48041
|
+
).action(async (filebox, item, opts) => {
|
|
48042
|
+
const itemId = parsePositiveIntArg2(item, "item id");
|
|
48043
|
+
await preflightConfirmToken(
|
|
48044
|
+
pollItemConfirmAction(filebox, itemId, scopeOf(opts)),
|
|
48045
|
+
opts.confirmToken
|
|
47864
48046
|
);
|
|
48047
|
+
await withConnectorsSetup(opts, async (setup) => {
|
|
48048
|
+
await runPollSkip({
|
|
48049
|
+
client: setup.client,
|
|
48050
|
+
filebox,
|
|
48051
|
+
itemId,
|
|
48052
|
+
deadline: {
|
|
48053
|
+
pause: () => setup.pauseTimeout(),
|
|
48054
|
+
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
48055
|
+
},
|
|
48056
|
+
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
48057
|
+
scope: scopeOf(opts),
|
|
48058
|
+
targetArgs: targetArgsOf(opts),
|
|
48059
|
+
asJson: opts.json === true,
|
|
48060
|
+
signal: setup.signal,
|
|
48061
|
+
out: process.stdout
|
|
48062
|
+
});
|
|
48063
|
+
});
|
|
47865
48064
|
});
|
|
48065
|
+
addCommonFlags(skipCmd, "Output the item as JSON");
|
|
48066
|
+
const seedCmd = addConfirmTokenFlag(
|
|
48067
|
+
poll2.command("seed").description(
|
|
48068
|
+
"Mark items modified at or before --until as already landed (cut-over; token, no dialog)"
|
|
48069
|
+
).argument("<filebox>", "filebox id or uuid").requiredOption(
|
|
48070
|
+
"--until <timestamp>",
|
|
48071
|
+
"date or timestamp with a zone, e.g. 2026-10-08T17:30:00Z"
|
|
48072
|
+
).option(
|
|
48073
|
+
"--dry-run",
|
|
48074
|
+
"count the already-discovered items it would mark, without changing anything",
|
|
48075
|
+
false
|
|
48076
|
+
).addHelpText("after", `
|
|
48077
|
+
${POLL_CONFIRM_HELP}`)
|
|
48078
|
+
).action(
|
|
48079
|
+
async (filebox, opts) => {
|
|
48080
|
+
const until = parseTimestampFlag(opts.until, "--until");
|
|
48081
|
+
const dryRun = opts.dryRun === true;
|
|
48082
|
+
if (!dryRun) {
|
|
48083
|
+
await preflightConfirmToken(
|
|
48084
|
+
pollConfirmAction(filebox, "seed", scopeOf(opts), { until }),
|
|
48085
|
+
opts.confirmToken
|
|
48086
|
+
);
|
|
48087
|
+
}
|
|
48088
|
+
await withConnectorsSetup(opts, async (setup) => {
|
|
48089
|
+
await runPollSeed({
|
|
48090
|
+
client: setup.client,
|
|
48091
|
+
filebox,
|
|
48092
|
+
until,
|
|
48093
|
+
untilArg: opts.until.trim(),
|
|
48094
|
+
dryRun,
|
|
48095
|
+
deadline: {
|
|
48096
|
+
pause: () => setup.pauseTimeout(),
|
|
48097
|
+
resume: () => setup.extendTimeout(DEFAULT_CALL_TIMEOUT_MS)
|
|
48098
|
+
},
|
|
48099
|
+
...opts.confirmToken !== void 0 ? { token: opts.confirmToken } : {},
|
|
48100
|
+
scope: scopeOf(opts),
|
|
48101
|
+
targetArgs: targetArgsOf(opts),
|
|
48102
|
+
asJson: opts.json === true,
|
|
48103
|
+
signal: setup.signal,
|
|
48104
|
+
out: process.stdout
|
|
48105
|
+
});
|
|
48106
|
+
});
|
|
48107
|
+
}
|
|
48108
|
+
);
|
|
48109
|
+
addCommonFlags(seedCmd, "Output the binding (or the dry-run count) as JSON");
|
|
47866
48110
|
}
|
|
47867
48111
|
|
|
47868
48112
|
// src/commands/connectors.ts
|
|
@@ -48523,6 +48767,7 @@ ${TLS_VPN_HELP}`);
|
|
|
48523
48767
|
registerDevFetch(connectors);
|
|
48524
48768
|
registerConnectorsPoll(connectors);
|
|
48525
48769
|
registerConnectorsTasks(connectors);
|
|
48770
|
+
registerConnectorsAws(connectors);
|
|
48526
48771
|
}
|
|
48527
48772
|
|
|
48528
48773
|
// src/types/dashboards.ts
|
|
@@ -53994,13 +54239,13 @@ async function runReport(cfg) {
|
|
|
53994
54239
|
function collectFile(value, previous) {
|
|
53995
54240
|
return previous.concat([value]);
|
|
53996
54241
|
}
|
|
53997
|
-
async function withOnboardingClient(opts,
|
|
54242
|
+
async function withOnboardingClient(opts, run2, setupOptions = {}) {
|
|
53998
54243
|
const { serverKey, host } = resolveOnboardingTarget(opts.server);
|
|
53999
54244
|
const callOptions = {};
|
|
54000
54245
|
if (setupOptions.timeoutMs !== void 0) callOptions.timeoutMs = setupOptions.timeoutMs;
|
|
54001
54246
|
const setup = await setupCall(serverKey, opts.account, callOptions);
|
|
54002
54247
|
try {
|
|
54003
|
-
await
|
|
54248
|
+
await run2(setup.client, host, setup.signal);
|
|
54004
54249
|
} finally {
|
|
54005
54250
|
setup.cleanup();
|
|
54006
54251
|
}
|
|
@@ -69431,8 +69676,8 @@ async function runExtractFromFiles(cfg) {
|
|
|
69431
69676
|
const p = await preflightFile(f);
|
|
69432
69677
|
parts.push({ blob: p.blob, filename: p.filename, sourcePath: f });
|
|
69433
69678
|
}
|
|
69434
|
-
const
|
|
69435
|
-
const { results: allResults, sourceFiles, outcomes, abortedMidRun } =
|
|
69679
|
+
const run2 = cfg.useAsync === false ? await extractSync(cfg, parts) : await extractAsync(cfg, parts);
|
|
69680
|
+
const { results: allResults, sourceFiles, outcomes, abortedMidRun } = run2;
|
|
69436
69681
|
await emitExtractionDraft(cfg, allResults, sourceFiles, outcomes, abortedMidRun);
|
|
69437
69682
|
}
|
|
69438
69683
|
async function extractAsync(cfg, parts) {
|
|
@@ -71550,8 +71795,8 @@ function querySuffix(domain) {
|
|
|
71550
71795
|
if (domain === void 0 || domain === "") return "";
|
|
71551
71796
|
return `?domain=${encodeURIComponent(domain)}`;
|
|
71552
71797
|
}
|
|
71553
|
-
function required(out,
|
|
71554
|
-
if (out === null) throw new Error(`${
|
|
71798
|
+
function required(out, call3) {
|
|
71799
|
+
if (out === null) throw new Error(`${call3}: empty response body`);
|
|
71555
71800
|
return out;
|
|
71556
71801
|
}
|
|
71557
71802
|
|
|
@@ -73428,6 +73673,175 @@ function registerPalettesCommand(root) {
|
|
|
73428
73673
|
addAccountFlag(createCmd);
|
|
73429
73674
|
}
|
|
73430
73675
|
|
|
73676
|
+
// src/api/businessCalendars.ts
|
|
73677
|
+
function buildAuditQs(q) {
|
|
73678
|
+
if (q === void 0) return "";
|
|
73679
|
+
const sp = new URLSearchParams();
|
|
73680
|
+
if (q.since !== void 0) sp.set("since", q.since);
|
|
73681
|
+
if (q.until !== void 0) sp.set("until", q.until);
|
|
73682
|
+
if (q.action !== void 0) sp.set("action", q.action);
|
|
73683
|
+
if (q.actor_user_id !== void 0) sp.set("actor_user_id", String(q.actor_user_id));
|
|
73684
|
+
if (q.page !== void 0) sp.set("page", String(q.page));
|
|
73685
|
+
if (q.page_size !== void 0) sp.set("page_size", String(q.page_size));
|
|
73686
|
+
const s = sp.toString();
|
|
73687
|
+
return s === "" ? "" : `?${s}`;
|
|
73688
|
+
}
|
|
73689
|
+
async function listAudit(client, calendarId, query, options) {
|
|
73690
|
+
if (calendarId === "") {
|
|
73691
|
+
throw new Error("calendarId must be a non-empty string");
|
|
73692
|
+
}
|
|
73693
|
+
const path13 = `/api/business-calendar/v1/business-calendars/${encodeURIComponent(
|
|
73694
|
+
calendarId
|
|
73695
|
+
)}/audit${buildAuditQs(query)}`;
|
|
73696
|
+
const out = await client.getJSON(path13, options);
|
|
73697
|
+
if (out === null) {
|
|
73698
|
+
return { count: 0, results: [] };
|
|
73699
|
+
}
|
|
73700
|
+
return out;
|
|
73701
|
+
}
|
|
73702
|
+
|
|
73703
|
+
// src/commands/calendars.ts
|
|
73704
|
+
var KNOWN_ACTIONS = /* @__PURE__ */ new Set([
|
|
73705
|
+
"create",
|
|
73706
|
+
"edit",
|
|
73707
|
+
"archive",
|
|
73708
|
+
"restore",
|
|
73709
|
+
"add_exception",
|
|
73710
|
+
"remove_exception"
|
|
73711
|
+
]);
|
|
73712
|
+
function parseAuditAction(v) {
|
|
73713
|
+
if (!KNOWN_ACTIONS.has(v)) {
|
|
73714
|
+
throw new Error(`invalid --action "${v}" (expected one of: ${[...KNOWN_ACTIONS].join(", ")})`);
|
|
73715
|
+
}
|
|
73716
|
+
return v;
|
|
73717
|
+
}
|
|
73718
|
+
function parsePositiveInt4(flag, v) {
|
|
73719
|
+
const n = Number(v);
|
|
73720
|
+
if (!Number.isInteger(n) || String(n) !== v || n <= 0) {
|
|
73721
|
+
throw new Error(`invalid ${flag} "${v}" (expected: positive integer)`);
|
|
73722
|
+
}
|
|
73723
|
+
return n;
|
|
73724
|
+
}
|
|
73725
|
+
async function runCalendarsAuditList(cfg) {
|
|
73726
|
+
if (cfg.calendarId === "") {
|
|
73727
|
+
throw new Error("<calendar_id> is required");
|
|
73728
|
+
}
|
|
73729
|
+
const resp = await listAudit(
|
|
73730
|
+
cfg.client,
|
|
73731
|
+
cfg.calendarId,
|
|
73732
|
+
{
|
|
73733
|
+
since: cfg.since,
|
|
73734
|
+
until: cfg.until,
|
|
73735
|
+
action: cfg.action,
|
|
73736
|
+
actor_user_id: cfg.actorUserId,
|
|
73737
|
+
page: cfg.page,
|
|
73738
|
+
page_size: cfg.limit
|
|
73739
|
+
},
|
|
73740
|
+
{ signal: cfg.signal }
|
|
73741
|
+
);
|
|
73742
|
+
if (cfg.asJson) {
|
|
73743
|
+
writeJSONIndented(cfg.out, resp);
|
|
73744
|
+
return;
|
|
73745
|
+
}
|
|
73746
|
+
renderAuditTable(cfg.out, resp);
|
|
73747
|
+
}
|
|
73748
|
+
function writeTable44(out, rows) {
|
|
73749
|
+
if (rows.length === 0) return;
|
|
73750
|
+
const widths = [];
|
|
73751
|
+
for (const r of rows) {
|
|
73752
|
+
for (let i = 0; i < r.cells.length; i++) {
|
|
73753
|
+
const cell = r.cells[i] ?? "";
|
|
73754
|
+
if ((widths[i] ?? 0) < cell.length) widths[i] = cell.length;
|
|
73755
|
+
}
|
|
73756
|
+
}
|
|
73757
|
+
for (const r of rows) {
|
|
73758
|
+
const parts = [];
|
|
73759
|
+
for (let i = 0; i < r.cells.length; i++) {
|
|
73760
|
+
const cell = r.cells[i] ?? "";
|
|
73761
|
+
if (i === r.cells.length - 1) {
|
|
73762
|
+
parts.push(cell);
|
|
73763
|
+
} else {
|
|
73764
|
+
parts.push(cell.padEnd((widths[i] ?? 0) + 2, " "));
|
|
73765
|
+
}
|
|
73766
|
+
}
|
|
73767
|
+
out.write(parts.join("") + "\n");
|
|
73768
|
+
}
|
|
73769
|
+
}
|
|
73770
|
+
function renderActor(row) {
|
|
73771
|
+
const email = row.actor.email ?? "";
|
|
73772
|
+
return email === "" ? `#${row.actor.id}` : `${email} (#${row.actor.id})`;
|
|
73773
|
+
}
|
|
73774
|
+
function renderFieldSummary(row) {
|
|
73775
|
+
const n = row.field_changes.length;
|
|
73776
|
+
if (n === 0) return "(none)";
|
|
73777
|
+
if (n === 1) return row.field_changes[0]?.field ?? "(1 field)";
|
|
73778
|
+
return `${n} fields`;
|
|
73779
|
+
}
|
|
73780
|
+
function renderAuditTable(out, resp) {
|
|
73781
|
+
if (resp.results.length === 0) {
|
|
73782
|
+
out.write(`(no audit rows; count=${resp.count})
|
|
73783
|
+
`);
|
|
73784
|
+
return;
|
|
73785
|
+
}
|
|
73786
|
+
const rows = [{ cells: ["ID", "TIMESTAMP", "ACTION", "VERSION", "ACTOR", "CHANGES"] }];
|
|
73787
|
+
for (const r of resp.results) {
|
|
73788
|
+
rows.push({
|
|
73789
|
+
cells: [
|
|
73790
|
+
String(r.id),
|
|
73791
|
+
r.timestamp,
|
|
73792
|
+
r.action,
|
|
73793
|
+
String(r.resulting_version),
|
|
73794
|
+
renderActor(r),
|
|
73795
|
+
renderFieldSummary(r)
|
|
73796
|
+
]
|
|
73797
|
+
});
|
|
73798
|
+
}
|
|
73799
|
+
writeTable44(out, rows);
|
|
73800
|
+
if (resp.count > resp.results.length) {
|
|
73801
|
+
out.write(`
|
|
73802
|
+
(showing ${resp.results.length} of ${resp.count}; use --limit / --page)
|
|
73803
|
+
`);
|
|
73804
|
+
}
|
|
73805
|
+
}
|
|
73806
|
+
function registerCalendarsCommand(root) {
|
|
73807
|
+
const cmd = root.command("calendars").description("Business-calendar (dr-business-calendar) operations");
|
|
73808
|
+
const audit = cmd.command("audit").description("Calendar-change audit log (FR-L13)");
|
|
73809
|
+
const listCmd = audit.command("list").description(
|
|
73810
|
+
"List calendar-change audit rows for one calendar (GET /v1/business-calendars/{id}/audit; isAdminOrSuperAdmin)"
|
|
73811
|
+
).argument("<calendar_id>", "Calendar id (UUID, any non-empty string)").option("--since <ISO>", "Only rows at or after this ISO timestamp").option("--until <ISO>", "Only rows strictly before this ISO timestamp").option(
|
|
73812
|
+
"--action <enum>",
|
|
73813
|
+
"Only rows for one action: create | edit | archive | restore | add_exception | remove_exception"
|
|
73814
|
+
).option("--actor-user-id <id>", "Only rows written by this user id (positive integer)").option("--limit <N>", "Max rows per page (positive integer; server page_size)").option("--page <N>", "Page number (positive integer; defaults to server page 1)").option("--json", "Output raw JSON instead of a table", false).action(async (calendarId, opts) => {
|
|
73815
|
+
if (calendarId === "") {
|
|
73816
|
+
throw new Error("<calendar_id> is required");
|
|
73817
|
+
}
|
|
73818
|
+
const action = opts.action === void 0 ? void 0 : parseAuditAction(opts.action);
|
|
73819
|
+
const actorUserId = opts.actorUserId === void 0 ? void 0 : parsePositiveInt4("--actor-user-id", opts.actorUserId);
|
|
73820
|
+
const limit = opts.limit === void 0 ? void 0 : parsePositiveInt4("--limit", opts.limit);
|
|
73821
|
+
const page = opts.page === void 0 ? void 0 : parsePositiveInt4("--page", opts.page);
|
|
73822
|
+
const setup = await setupCall(opts.server, opts.account);
|
|
73823
|
+
try {
|
|
73824
|
+
await runCalendarsAuditList({
|
|
73825
|
+
client: setup.client,
|
|
73826
|
+
calendarId,
|
|
73827
|
+
since: opts.since,
|
|
73828
|
+
until: opts.until,
|
|
73829
|
+
action,
|
|
73830
|
+
actorUserId,
|
|
73831
|
+
limit,
|
|
73832
|
+
page,
|
|
73833
|
+
asJson: opts.json === true,
|
|
73834
|
+
signal: setup.signal,
|
|
73835
|
+
out: process.stdout
|
|
73836
|
+
});
|
|
73837
|
+
} finally {
|
|
73838
|
+
setup.cleanup();
|
|
73839
|
+
}
|
|
73840
|
+
});
|
|
73841
|
+
addServerFlag(listCmd);
|
|
73842
|
+
addAccountFlag(listCmd);
|
|
73843
|
+
}
|
|
73844
|
+
|
|
73431
73845
|
// src/commands/appSpace.ts
|
|
73432
73846
|
import { buildRootCommand as buildAppSpaceRoot } from "@datarails-ai/dr-appspace-cli/dist/cli.js";
|
|
73433
73847
|
function registerAppSpaceCommand(root) {
|
|
@@ -74041,6 +74455,7 @@ function buildRootCommand() {
|
|
|
74041
74455
|
registerMultiProductCommand(root);
|
|
74042
74456
|
registerDeskCommand(root);
|
|
74043
74457
|
registerPalettesCommand(root);
|
|
74458
|
+
registerCalendarsCommand(root);
|
|
74044
74459
|
registerAppSpaceCommand(root);
|
|
74045
74460
|
return root;
|
|
74046
74461
|
}
|