@datarails-ai/dr-cli 1.6.99 → 1.6.100
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +83 -15
- package/dist/scopes-map.json +1 -1
- package/package.json +1 -1
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.100",
|
|
12
12
|
description: "Datarails CLI",
|
|
13
13
|
type: "module",
|
|
14
14
|
engines: {
|
|
@@ -69,7 +69,7 @@ var package_default = {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
// src/buildInfo.ts
|
|
72
|
-
var BUILD_TIME = "2026-10-05T12:
|
|
72
|
+
var BUILD_TIME = "2026-10-05T12:55:55.787Z";
|
|
73
73
|
function formatBuildTime(iso) {
|
|
74
74
|
if (iso === "") return "";
|
|
75
75
|
const d = new Date(iso);
|
|
@@ -5007,7 +5007,7 @@ async function runLogout(cfg) {
|
|
|
5007
5007
|
const session2 = await cfg.store.getSession(key, meta.email);
|
|
5008
5008
|
if (session2 === void 0) continue;
|
|
5009
5009
|
try {
|
|
5010
|
-
await revoker(host2, session2);
|
|
5010
|
+
writeNotes(cfg.out, await revoker(host2, session2));
|
|
5011
5011
|
} catch {
|
|
5012
5012
|
}
|
|
5013
5013
|
await cfg.store.removeSession(key, meta.email);
|
|
@@ -5040,32 +5040,95 @@ async function runLogout(cfg) {
|
|
|
5040
5040
|
}
|
|
5041
5041
|
const host = resolve(cfg.serverKey).host;
|
|
5042
5042
|
try {
|
|
5043
|
-
await revoker(host, session);
|
|
5043
|
+
writeNotes(cfg.out, await revoker(host, session));
|
|
5044
5044
|
} catch {
|
|
5045
5045
|
}
|
|
5046
5046
|
await cfg.store.removeSession(cfg.serverKey, email);
|
|
5047
5047
|
cfg.out.write(`Logged out of ${cfg.serverKey} as ${email}.
|
|
5048
5048
|
`);
|
|
5049
5049
|
}
|
|
5050
|
+
function writeNotes(out, notes) {
|
|
5051
|
+
for (const line of notes ?? []) out.write(`${line}
|
|
5052
|
+
`);
|
|
5053
|
+
}
|
|
5054
|
+
var REVOKE_TIMEOUT_MS = 5e3;
|
|
5050
5055
|
async function bestEffortLogout(host, session) {
|
|
5051
|
-
if (host === ""
|
|
5056
|
+
if (host === "") return [];
|
|
5052
5057
|
const base = host.replace(/\/+$/, "");
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5058
|
+
if (usesApiKey(session)) {
|
|
5059
|
+
return [
|
|
5060
|
+
"Note: the API key is still valid server-side until it is revoked where it was issued; only the local copy was removed."
|
|
5061
|
+
];
|
|
5062
|
+
}
|
|
5063
|
+
const refresh = session.jwtRefresh ?? "";
|
|
5064
|
+
const hasCookieSession = !isCookielessSession(session) && session.sessionId !== "" && session.csrfToken !== "";
|
|
5065
|
+
if (refresh === "" && !hasCookieSession) {
|
|
5066
|
+
return [
|
|
5067
|
+
`Warning: no refresh token is stored for this identity, so nothing was revoked on ${base}; only the local copy was removed.`
|
|
5068
|
+
];
|
|
5069
|
+
}
|
|
5070
|
+
const [refreshFailure, sessionFailure] = await Promise.all([
|
|
5071
|
+
refresh === "" ? void 0 : revokeRefreshToken(base, refresh),
|
|
5072
|
+
hasCookieSession ? endCookieSession(base, session) : void 0
|
|
5073
|
+
]);
|
|
5074
|
+
const notes = [];
|
|
5075
|
+
if (refreshFailure !== void 0) {
|
|
5076
|
+
notes.push(
|
|
5077
|
+
`Warning: could not revoke the refresh token on ${base} (${refreshFailure}); it stays valid server-side until it expires.`
|
|
5078
|
+
);
|
|
5079
|
+
}
|
|
5080
|
+
if (sessionFailure !== void 0) {
|
|
5081
|
+
notes.push(
|
|
5082
|
+
`Warning: could not end the browser session on ${base} (${sessionFailure}); it stays valid server-side until it expires.`
|
|
5083
|
+
);
|
|
5084
|
+
}
|
|
5085
|
+
return notes;
|
|
5086
|
+
}
|
|
5087
|
+
async function revokeRefreshToken(base, refresh) {
|
|
5088
|
+
return withTimeout(async (signal) => {
|
|
5089
|
+
const res = await fetch(`${base}/jwt/api/token/revoke/`, {
|
|
5090
|
+
method: "POST",
|
|
5091
|
+
headers: { "Content-Type": "application/json" },
|
|
5092
|
+
body: JSON.stringify({ refresh }),
|
|
5093
|
+
signal
|
|
5094
|
+
});
|
|
5095
|
+
await discardBody(res);
|
|
5096
|
+
return res.status === 204 ? void 0 : `HTTP ${res.status}`;
|
|
5097
|
+
});
|
|
5098
|
+
}
|
|
5099
|
+
async function endCookieSession(base, session) {
|
|
5100
|
+
return withTimeout(async (signal) => {
|
|
5101
|
+
const res = await fetch(`${base}/api/accounts/logout/`, {
|
|
5057
5102
|
method: "POST",
|
|
5058
5103
|
headers: {
|
|
5059
5104
|
Cookie: `sessionid=${session.sessionId}; csrftoken=${session.csrfToken}`,
|
|
5060
|
-
"X-CSRFToken": session.csrfToken
|
|
5105
|
+
"X-CSRFToken": session.csrfToken,
|
|
5106
|
+
Referer: `${base}/`
|
|
5061
5107
|
},
|
|
5062
|
-
|
|
5108
|
+
redirect: "manual",
|
|
5109
|
+
signal
|
|
5063
5110
|
});
|
|
5111
|
+
await discardBody(res);
|
|
5112
|
+
return res.status >= 200 && res.status < 400 ? void 0 : `HTTP ${res.status}`;
|
|
5113
|
+
});
|
|
5114
|
+
}
|
|
5115
|
+
async function withTimeout(run) {
|
|
5116
|
+
const controller = new AbortController();
|
|
5117
|
+
const timeout = setTimeout(() => controller.abort(), REVOKE_TIMEOUT_MS);
|
|
5118
|
+
try {
|
|
5119
|
+
return await run(controller.signal);
|
|
5064
5120
|
} catch {
|
|
5121
|
+
return controller.signal.aborted ? "timed out" : "network error";
|
|
5065
5122
|
} finally {
|
|
5066
5123
|
clearTimeout(timeout);
|
|
5067
5124
|
}
|
|
5068
5125
|
}
|
|
5126
|
+
async function discardBody(res) {
|
|
5127
|
+
try {
|
|
5128
|
+
await res.text();
|
|
5129
|
+
} catch {
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5069
5132
|
|
|
5070
5133
|
// src/commands/login.ts
|
|
5071
5134
|
import { Option } from "commander";
|
|
@@ -5172,7 +5235,7 @@ function tryParseErrorCode(text2) {
|
|
|
5172
5235
|
return typeof errVal === "string" ? errVal : "";
|
|
5173
5236
|
}
|
|
5174
5237
|
async function pollForToken(host, deviceCode, options = {}) {
|
|
5175
|
-
return poll(host, PATHS.legacy.token, deviceCode, options, (text2) => {
|
|
5238
|
+
return poll(host, PATHS.legacy.token, deviceCode, options, {}, (text2) => {
|
|
5176
5239
|
const tok = parseTokenResponse(text2);
|
|
5177
5240
|
if (!tok.sessionId || !tok.csrfToken) {
|
|
5178
5241
|
throw new Error("token response missing session_id or csrf_token");
|
|
@@ -5181,7 +5244,7 @@ async function pollForToken(host, deviceCode, options = {}) {
|
|
|
5181
5244
|
});
|
|
5182
5245
|
}
|
|
5183
5246
|
async function pollForV2Token(host, deviceCode, options = {}) {
|
|
5184
|
-
return poll(host, PATHS.v2.token, deviceCode, options, (text2) => {
|
|
5247
|
+
return poll(host, PATHS.v2.token, deviceCode, options, V2_TERMINAL_HINTS, (text2) => {
|
|
5185
5248
|
const obj = parseObject(text2);
|
|
5186
5249
|
const access = typeof obj.access === "string" ? obj.access : "";
|
|
5187
5250
|
const refresh = typeof obj.refresh === "string" ? obj.refresh : "";
|
|
@@ -5191,7 +5254,11 @@ async function pollForV2Token(host, deviceCode, options = {}) {
|
|
|
5191
5254
|
return { access, refresh };
|
|
5192
5255
|
});
|
|
5193
5256
|
}
|
|
5194
|
-
|
|
5257
|
+
var V2_TERMINAL_HINTS = {
|
|
5258
|
+
access_denied: " (the login was refused, expired, or already used: run `dr login` again)",
|
|
5259
|
+
token_issuance_failed: " (the server could not issue the token: run `dr login` again to start a new login)"
|
|
5260
|
+
};
|
|
5261
|
+
async function poll(host, path11, deviceCode, options, terminalHints, onSuccess) {
|
|
5195
5262
|
const interval = options.interval ?? 2e3;
|
|
5196
5263
|
const maxWait = options.maxWait ?? 6e5;
|
|
5197
5264
|
const now = options.now ?? Date.now;
|
|
@@ -5227,7 +5294,8 @@ async function poll(host, path11, deviceCode, options, onSuccess) {
|
|
|
5227
5294
|
continue;
|
|
5228
5295
|
}
|
|
5229
5296
|
if (errCode) {
|
|
5230
|
-
|
|
5297
|
+
const hint = Object.hasOwn(terminalHints, errCode) ? terminalHints[errCode] : "";
|
|
5298
|
+
throw new Error(`token error: ${errCode}${hint}`);
|
|
5231
5299
|
}
|
|
5232
5300
|
throw new Error(`token HTTP ${res.status}: ${text2.trim()}`);
|
|
5233
5301
|
}
|
package/dist/scopes-map.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"cliVersion": "1.6.
|
|
3
|
+
"cliVersion": "1.6.100",
|
|
4
4
|
"_contract": "Resolve a command path through `aliases` (canonical prefix + alias segment -> canonical path) before any lookup. A path listed in `pendingCommands`, or whose first segment is a key of `pending`, is UNDECIDED, not unrestricted: fail closed or skip it, and never read a path's absence from `commands` as 'no requirement'. An entry carrying `denied` is UNAVAILABLE to any AGENT-SCOPED credential — one whose `scopes` claim carries at least one `resource:action` entry; a claim that is absent, empty, capability-only or wholly unparseable is UNRESTRICTED, and this mark does not apply to it — whatever else that entry says: it is not a ceiling and it is not always-available, its `denied.reason` says why and `denied.until` names the ticket that lifts it, and the absence of `requires` on it must NEVER be read as 'no requirement'. Only an entry in `commands` with an empty `requires` means always-available.",
|
|
5
5
|
"commands": {
|
|
6
6
|
"login": {
|