xaq-cli 0.1.0-nightly.86 → 0.1.0-nightly.87
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
CHANGED
|
@@ -59,7 +59,7 @@ const SERVER_GROUP = "Run xaq:";
|
|
|
59
59
|
const AGENT_GROUP = "On an agent's machine:";
|
|
60
60
|
withServerOptions(program.command("start", { isDefault: true }).description("run xaq in this terminal and print the setup link (the default command)").summary("run it in this terminal (the default command)").helpGroup(SERVER_GROUP)).option("--no-open", "don't open a browser on first run").option("--dev", "development: API only, the console runs on Vite (:5173)").allowExcessArguments().action(async (o, cmd) => {
|
|
61
61
|
if (cmd.args.length) throw new Error(unknownCommand(cmd.args[0]));
|
|
62
|
-
const { runForeground } = await import("./server-
|
|
62
|
+
const { runForeground } = await import("./server-DrcmvN-U.js");
|
|
63
63
|
await runForeground({
|
|
64
64
|
...configInput(o),
|
|
65
65
|
dev: !!o.dev,
|
|
@@ -8384,7 +8384,7 @@ var GoogleRuntime = class {
|
|
|
8384
8384
|
let uncertain = null;
|
|
8385
8385
|
for (let attempt = 0;; attempt++) {
|
|
8386
8386
|
if (opts.cost) await this.bucket(accountId).take(opts.cost);
|
|
8387
|
-
const token = await this.tokens.accessToken(accountId, { force: forced });
|
|
8387
|
+
const token = opts.token ? await opts.token(forced) : await this.tokens.accessToken(accountId, { force: forced });
|
|
8388
8388
|
let err;
|
|
8389
8389
|
try {
|
|
8390
8390
|
const res = await fetch(url, {
|
|
@@ -8417,7 +8417,7 @@ var GoogleRuntime = class {
|
|
|
8417
8417
|
}
|
|
8418
8418
|
if (err.status === 401 && !forced) {
|
|
8419
8419
|
forced = true;
|
|
8420
|
-
this.tokens.invalidate(accountId);
|
|
8420
|
+
if (!opts.token) this.tokens.invalidate(accountId);
|
|
8421
8421
|
continue;
|
|
8422
8422
|
}
|
|
8423
8423
|
if (!(idempotent ? isRetryable(err) : rejectedBeforeProcessing(err)) || attempt >= 4) {
|
|
@@ -8596,11 +8596,27 @@ function classifyCalendarError(err) {
|
|
|
8596
8596
|
/** The enable link Google put in a SERVICE_DISABLED error, if any. */
|
|
8597
8597
|
const activationUrl = (err) => err.details.metadata?.activationUrl ?? null;
|
|
8598
8598
|
//#endregion
|
|
8599
|
+
//#region server/google/scoped.ts
|
|
8600
|
+
/** Whether the account has granted xaq every one of `scopes` (as last recorded). False for an unknown account. */
|
|
8601
|
+
function accountHasScopes(ctx, accountId, scopes) {
|
|
8602
|
+
const row = getRow(ctx, accountId);
|
|
8603
|
+
return !!row && hasScopes(accountScopes(row), scopes);
|
|
8604
|
+
}
|
|
8605
|
+
/**
|
|
8606
|
+
* An access token for the account limited to `scopes` (a subset of what it granted), with its expiry in epoch ms and
|
|
8607
|
+
* the scopes Google says it has. Cached per account and scope set until it nearly expires; one refresh in flight.
|
|
8608
|
+
* Throws ScopesNotGrantedError (not granted: ask for consent), AccountNeedsReauthError (the sign-in is gone) or
|
|
8609
|
+
* DownscopeError (Google's answer didn't prove the limit; never returned).
|
|
8610
|
+
*/
|
|
8611
|
+
function googleAccessToken(ctx, accountId, scopes, opts = {}) {
|
|
8612
|
+
return googleRuntime(ctx).tokens.scopedAccessToken(accountId, scopes, opts);
|
|
8613
|
+
}
|
|
8614
|
+
//#endregion
|
|
8599
8615
|
//#region server/google/drive.ts
|
|
8600
|
-
/**
|
|
8601
|
-
const
|
|
8602
|
-
/** Whether
|
|
8603
|
-
const canReadDrive = (
|
|
8616
|
+
/** The scopes a Drive attachment's token carries: read-only Drive, nothing else. */
|
|
8617
|
+
const DRIVE_ATTACH_SCOPES = [DRIVE_READONLY_SCOPE];
|
|
8618
|
+
/** Whether the account has granted xaq the Drive read access attachments need (as last recorded). */
|
|
8619
|
+
const canReadDrive = (ctx, accountId) => accountHasScopes(ctx, accountId, DRIVE_ATTACH_SCOPES);
|
|
8604
8620
|
const FIELDS$1 = "id,name,mimeType,size,webViewLink,trashed,shortcutDetails";
|
|
8605
8621
|
/** Downloads take longer: 30 s plus 1 s per 256 KB they may carry, at most 10 minutes. */
|
|
8606
8622
|
const downloadTimeout = (maxBytes) => Math.min(6e5, 3e4 + Math.ceil(maxBytes / 262144) * 1e3);
|
|
@@ -8608,12 +8624,17 @@ function createDriveClient(ctx, accountId) {
|
|
|
8608
8624
|
const rt = googleRuntime(ctx);
|
|
8609
8625
|
const base = ctx.config.google.apisBase ?? "https://www.googleapis.com";
|
|
8610
8626
|
const path = (fileId, suffix = "") => `/drive/v3/files/${encodeURIComponent(fileId)}${suffix}`;
|
|
8627
|
+
const token = async (force) => (await googleAccessToken(ctx, accountId, DRIVE_ATTACH_SCOPES, { force })).accessToken;
|
|
8611
8628
|
return {
|
|
8612
|
-
getFile: (fileId) => rt.request(accountId, base, path(fileId), {
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8629
|
+
getFile: (fileId) => rt.request(accountId, base, path(fileId), {
|
|
8630
|
+
query: {
|
|
8631
|
+
fields: FIELDS$1,
|
|
8632
|
+
supportsAllDrives: true
|
|
8633
|
+
},
|
|
8634
|
+
token
|
|
8635
|
+
}),
|
|
8616
8636
|
download: (fileId, maxBytes) => rt.request(accountId, base, path(fileId), {
|
|
8637
|
+
token,
|
|
8617
8638
|
query: {
|
|
8618
8639
|
alt: "media",
|
|
8619
8640
|
supportsAllDrives: true
|
|
@@ -8623,6 +8644,7 @@ function createDriveClient(ctx, accountId) {
|
|
|
8623
8644
|
timeoutMs: downloadTimeout(maxBytes)
|
|
8624
8645
|
}),
|
|
8625
8646
|
exportFile: (fileId, mimeType, maxBytes) => rt.request(accountId, base, path(fileId, "/export"), {
|
|
8647
|
+
token,
|
|
8626
8648
|
query: { mimeType },
|
|
8627
8649
|
binary: true,
|
|
8628
8650
|
maxResponseBytes: maxBytes,
|
|
@@ -8636,15 +8658,10 @@ function createDriveClient(ctx, accountId) {
|
|
|
8636
8658
|
const GOOGLE_WORKSPACE_EXTENSION = "google-workspace";
|
|
8637
8659
|
/**
|
|
8638
8660
|
* The xaq Google account id (acc_…) the google-workspace connection `connectionId` is bound to, or null when it
|
|
8639
|
-
* isn't bound (or the
|
|
8640
|
-
*
|
|
8641
|
-
* TODO(C, feat/google-extension-auth): read C's binding here. C stores the account id on the connection or on its
|
|
8642
|
-
* credential; that shape wasn't visible when this was written, so until C's branch merges no connection counts as
|
|
8643
|
-
* bound and agentCanReadDrive is always false (attaching by drive_file_id is refused for every agent: fail closed).
|
|
8644
|
-
* Replace the body with the lookup, e.g. `SELECT google_account_id FROM connections WHERE id = ? AND preset = ?`.
|
|
8661
|
+
* isn't a google-workspace connection or isn't bound (no account picked yet, or the account was removed).
|
|
8645
8662
|
*/
|
|
8646
8663
|
function workspaceConnectionAccount(db, connectionId) {
|
|
8647
|
-
return null;
|
|
8664
|
+
return db.get("SELECT google_account_id FROM connections WHERE id = ? AND preset = ?", connectionId, "google-workspace")?.google_account_id ?? null;
|
|
8648
8665
|
}
|
|
8649
8666
|
//#endregion
|
|
8650
8667
|
//#region server/google/drive-access.ts
|
|
@@ -9471,6 +9488,9 @@ function planDriveFile(label, file, exportAs) {
|
|
|
9471
9488
|
}
|
|
9472
9489
|
/** Drive's error for a file id, in words the agent can act on. Anything unrecognised is returned unchanged. */
|
|
9473
9490
|
function driveError(err, label, fileId, accountEmail, file) {
|
|
9491
|
+
if (err instanceof ScopesNotGrantedError) return driveNotEnabled(accountEmail);
|
|
9492
|
+
if (err instanceof AccountNeedsReauthError) return new AttachmentError("needs_reauth", `The Google account ${accountEmail} needs to be reconnected in the xaq console before its Drive files can be attached.`);
|
|
9493
|
+
if (err instanceof DownscopeError) return new AttachmentError("drive_error", `${label}: Google didn't give xaq a Drive-only token for ${accountEmail}, so nothing was downloaded. Try again later.`);
|
|
9474
9494
|
if (!(err instanceof GoogleApiError)) return err;
|
|
9475
9495
|
const name = file?.name ?? fileId;
|
|
9476
9496
|
const link = file?.webViewLink ? ` Put its link in the body instead: ${file.webViewLink}` : "";
|
|
@@ -10663,7 +10683,7 @@ var AgentMail = class {
|
|
|
10663
10683
|
*/
|
|
10664
10684
|
driveFor(a) {
|
|
10665
10685
|
if (!agentCanReadDrive(this.ctx, this.principal.agentId, a.account.id)) return new AttachmentError("drive_not_granted", driveNotGrantedMessage(a.account.email));
|
|
10666
|
-
if (!canReadDrive(a.account.
|
|
10686
|
+
if (!canReadDrive(this.ctx, a.account.id)) return driveNotEnabled(a.account.email);
|
|
10667
10687
|
return createDriveClient(this.ctx, a.account.id);
|
|
10668
10688
|
}
|
|
10669
10689
|
/**
|
|
@@ -14158,17 +14178,6 @@ function createCalendarSetup(ctx) {
|
|
|
14158
14178
|
};
|
|
14159
14179
|
}
|
|
14160
14180
|
//#endregion
|
|
14161
|
-
//#region server/google/scoped.ts
|
|
14162
|
-
/**
|
|
14163
|
-
* An access token for the account limited to `scopes` (a subset of what it granted), with its expiry in epoch ms and
|
|
14164
|
-
* the scopes Google says it has. Cached per account and scope set until it nearly expires; one refresh in flight.
|
|
14165
|
-
* Throws ScopesNotGrantedError (not granted: ask for consent), AccountNeedsReauthError (the sign-in is gone) or
|
|
14166
|
-
* DownscopeError (Google's answer didn't prove the limit; never returned).
|
|
14167
|
-
*/
|
|
14168
|
-
function googleAccessToken(ctx, accountId, scopes, opts = {}) {
|
|
14169
|
-
return googleRuntime(ctx).tokens.scopedAccessToken(accountId, scopes, opts);
|
|
14170
|
-
}
|
|
14171
|
-
//#endregion
|
|
14172
14181
|
//#region server/google/apis.ts
|
|
14173
14182
|
/** The project ID from the Google step of the setup wizard, when it is one. */
|
|
14174
14183
|
function googleProjectId(ctx) {
|
|
@@ -24642,4 +24651,4 @@ function openBrowser(url) {
|
|
|
24642
24651
|
//#endregion
|
|
24643
24652
|
export { runForeground };
|
|
24644
24653
|
|
|
24645
|
-
//# sourceMappingURL=server-
|
|
24654
|
+
//# sourceMappingURL=server-DrcmvN-U.js.map
|