@indigoai-us/hq-cli 5.345.156 → 5.345.160
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/CHANGELOG.md +255 -0
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +110 -2
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +101 -5
- package/assets/scaffold/core/scripts/worktree.sh +4 -0
- package/dist/command-catalog.generated.d.ts +535 -10
- package/dist/command-catalog.generated.js +678 -31
- package/dist/command-registration-plan.d.ts +18 -0
- package/dist/command-registration-plan.js +4 -0
- package/dist/commands/_patterns.d.ts +17 -0
- package/dist/commands/_patterns.js +22 -0
- package/dist/commands/access.d.ts +6 -0
- package/dist/commands/access.js +18 -1
- package/dist/commands/acl-lock.d.ts +41 -0
- package/dist/commands/acl-lock.js +28 -0
- package/dist/commands/agent-enroll.d.ts +2 -0
- package/dist/commands/agent-enroll.js +9 -4
- package/dist/commands/agent-kit.js +8 -6
- package/dist/commands/agents.js +5 -11
- package/dist/commands/billing-sponsor.d.ts +4 -4
- package/dist/commands/billing-sponsor.js +11 -11
- package/dist/commands/billing-usage.d.ts +22 -8
- package/dist/commands/billing-usage.js +57 -22
- package/dist/commands/billing.d.ts +7 -7
- package/dist/commands/billing.js +13 -13
- package/dist/commands/bot.js +7 -4
- package/dist/commands/channel-suggest.d.ts +56 -0
- package/dist/commands/channel-suggest.js +170 -0
- package/dist/commands/channels.js +7 -1
- package/dist/commands/cloud-provision.js +2 -2
- package/dist/commands/cloud.d.ts +45 -0
- package/dist/commands/cloud.js +157 -17
- package/dist/commands/company.js +4 -3
- package/dist/commands/core-checkpoint-native.d.ts +18 -0
- package/dist/commands/core-checkpoint-native.js +67 -2
- package/dist/commands/core-pipeline-driver-native.d.ts +12 -1
- package/dist/commands/core-pipeline-driver-native.js +131 -5
- package/dist/commands/core.js +12 -0
- package/dist/commands/crm.js +83 -6
- package/dist/commands/db-remote-deps.js +12 -1
- package/dist/commands/deploy-access.d.ts +33 -0
- package/dist/commands/deploy-access.js +322 -0
- package/dist/commands/dm-attach.d.ts +106 -0
- package/dist/commands/dm-attach.js +184 -0
- package/dist/commands/dm.d.ts +37 -0
- package/dist/commands/dm.js +172 -10
- package/dist/commands/feedback-show.d.ts +40 -0
- package/dist/commands/feedback-show.js +261 -0
- package/dist/commands/feedback.d.ts +21 -0
- package/dist/commands/feedback.js +152 -2
- package/dist/commands/files.d.ts +24 -0
- package/dist/commands/files.js +71 -8
- package/dist/commands/integrations-connect.js +7 -3
- package/dist/commands/lanes-accounts.js +16 -1
- package/dist/commands/lanes-loop.js +14 -4
- package/dist/commands/lanes-message.js +6 -4
- package/dist/commands/lanes-resume.js +36 -13
- package/dist/commands/lanes-shared.d.ts +5 -0
- package/dist/commands/lanes-shared.js +13 -3
- package/dist/commands/lanes-watch.js +1 -0
- package/dist/commands/lanes.d.ts +20 -1
- package/dist/commands/lanes.js +201 -51
- package/dist/commands/meetings.d.ts +4 -4
- package/dist/commands/meetings.js +17 -8
- package/dist/commands/mesh.js +149 -1
- package/dist/commands/monitor.d.ts +1 -0
- package/dist/commands/monitor.js +4 -1
- package/dist/commands/repo.d.ts +41 -0
- package/dist/commands/repo.js +331 -0
- package/dist/commands/scan.d.ts +3 -1
- package/dist/commands/scan.js +7 -3
- package/dist/commands/secrets.d.ts +21 -0
- package/dist/commands/secrets.js +114 -14
- package/dist/commands/signals.js +17 -5
- package/dist/commands/skill.js +6 -6
- package/dist/commands/staff/listings.d.ts +41 -0
- package/dist/commands/staff/listings.js +160 -0
- package/dist/commands/staff/reset-test-account.d.ts +3 -2
- package/dist/commands/staff/reset-test-account.js +4 -3
- package/dist/commands/whoami.js +1 -1
- package/dist/hook-engine-path.js +13 -2
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +3392 -1243
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/agent-kit/run/inbox.js +2 -1
- package/dist/lib/agent-kit/wake-secrets.d.ts +7 -0
- package/dist/lib/agent-kit/wake-secrets.js +32 -0
- package/dist/lib/agent-kit/wake.d.ts +4 -0
- package/dist/lib/agent-kit/wake.js +49 -3
- package/dist/lib/billing/plan-limit-refusals.d.ts +13 -3
- package/dist/lib/billing/plan-limit-refusals.js +26 -3
- package/dist/lib/billing/plan-limit-upgrade-url.d.ts +8 -0
- package/dist/lib/billing/plan-limit-upgrade-url.js +18 -0
- package/dist/lib/billing/plan-lock.d.ts +22 -6
- package/dist/lib/billing/plan-lock.js +58 -30
- package/dist/lib/billing/usage-cost.d.ts +24 -2
- package/dist/lib/billing/usage-cost.js +103 -3
- package/dist/lib/billing/usage-only.d.ts +37 -1
- package/dist/lib/billing/usage-only.js +89 -4
- package/dist/lib/billing/usage-pause.d.ts +27 -0
- package/dist/lib/billing/usage-pause.js +50 -0
- package/dist/lib/bot/inflight.d.ts +7 -1
- package/dist/lib/bot/inflight.js +10 -2
- package/dist/lib/bot/prompt.js +3 -0
- package/dist/lib/bot/run.js +72 -20
- package/dist/lib/bot/session.d.ts +14 -0
- package/dist/lib/bot/session.js +70 -0
- package/dist/lib/capacity/collectors/claude-oauth.d.ts +6 -0
- package/dist/lib/capacity/collectors/claude-oauth.js +12 -0
- package/dist/lib/capacity/lane-login.d.ts +6 -0
- package/dist/lib/capacity/lane-login.js +5 -0
- package/dist/lib/core-utils/worktree.js +2 -0
- package/dist/lib/daemon/exit-cause.d.ts +43 -0
- package/dist/lib/daemon/exit-cause.js +88 -0
- package/dist/lib/daemon/handoff.d.ts +2 -0
- package/dist/lib/daemon/handoff.js +6 -1
- package/dist/lib/daemon/hq-child-bootstrap.mjs +20 -3
- package/dist/lib/daemon/paths.d.ts +2 -0
- package/dist/lib/daemon/paths.js +1 -0
- package/dist/lib/daemon/run.js +13 -0
- package/dist/lib/daemon/supervisor.d.ts +54 -1
- package/dist/lib/daemon/supervisor.js +297 -21
- package/dist/lib/daemon/unit-holds.d.ts +22 -0
- package/dist/lib/daemon/unit-holds.js +86 -0
- package/dist/lib/db/control-plane.d.ts +5 -0
- package/dist/lib/db/control-plane.js +10 -0
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/hook-engine/resolve.d.ts +14 -0
- package/dist/lib/hook-engine/resolve.js +55 -1
- package/dist/lib/index-render/company-knowledge.js +1 -3
- package/dist/lib/index-render/projects.js +1 -3
- package/dist/lib/lanes/account-company.d.ts +28 -0
- package/dist/lib/lanes/account-company.js +53 -0
- package/dist/lib/lanes/accounts.d.ts +22 -3
- package/dist/lib/lanes/accounts.js +38 -2
- package/dist/lib/lanes/claude-balance.d.ts +6 -1
- package/dist/lib/lanes/claude-balance.js +6 -1
- package/dist/lib/lanes/claude-credential.d.ts +78 -0
- package/dist/lib/lanes/claude-credential.js +163 -0
- package/dist/lib/lanes/compaction.d.ts +2 -0
- package/dist/lib/lanes/compaction.js +9 -0
- package/dist/lib/lanes/config.js +6 -0
- package/dist/lib/lanes/envelope-repair-dispatch.js +2 -1
- package/dist/lib/lanes/hooks-install.js +2 -1
- package/dist/lib/lanes/host-capacity.d.ts +2 -0
- package/dist/lib/lanes/host-capacity.js +1 -0
- package/dist/lib/lanes/keep-alive.d.ts +1 -1
- package/dist/lib/lanes/keep-alive.js +3 -1
- package/dist/lib/lanes/links.js +10 -4
- package/dist/lib/lanes/loop.js +16 -1
- package/dist/lib/lanes/node-binary.d.ts +17 -0
- package/dist/lib/lanes/node-binary.js +30 -0
- package/dist/lib/lanes/outpost-dispatch.d.ts +2 -1
- package/dist/lib/lanes/outpost-dispatch.js +1 -0
- package/dist/lib/lanes/reconcile.js +4 -0
- package/dist/lib/lanes/remote-control-lane.js +2 -0
- package/dist/lib/lanes/spawn.d.ts +3 -0
- package/dist/lib/lanes/spawn.js +119 -9
- package/dist/lib/lanes/store.d.ts +6 -0
- package/dist/lib/lanes/store.js +107 -18
- package/dist/lib/lanes/types.d.ts +2 -0
- package/dist/lib/lanes/watch.js +28 -5
- package/dist/lib/lanes/watcher-main.js +4 -3
- package/dist/lib/lanes/watcher.d.ts +4 -0
- package/dist/lib/lanes/watcher.js +110 -21
- package/dist/lib/mesh/api.d.ts +32 -0
- package/dist/lib/mesh/api.js +75 -0
- package/dist/lib/plan-limit-nag.d.ts +0 -16
- package/dist/lib/plan-limit-nag.js +17 -18
- package/dist/lib/secret-scan/detect.js +9 -0
- package/dist/lib/secret-scan/repo-config.d.ts +41 -0
- package/dist/lib/secret-scan/repo-config.js +237 -0
- package/dist/lib/secret-scan/rules.d.ts +7 -1
- package/dist/lib/secret-scan/rules.js +3 -1
- package/dist/lib/secret-scan/staged.d.ts +5 -0
- package/dist/lib/secret-scan/staged.js +19 -2
- package/dist/lib/secret-scan/types.d.ts +2 -0
- package/dist/lib/sync/server-owned-local-only.d.ts +29 -0
- package/dist/lib/sync/server-owned-local-only.js +75 -0
- package/dist/main.d.ts +0 -2
- package/dist/main.js +19 -57
- package/dist/utils/package-use-lease.js +27 -9
- package/dist/utils/plan-gate-error.d.ts +30 -14
- package/dist/utils/plan-gate-error.js +60 -46
- package/dist/utils/team-upgrade.d.ts +19 -6
- package/dist/utils/team-upgrade.js +37 -39
- package/dist/utils/version-gate.d.ts +7 -0
- package/dist/utils/version-gate.js +66 -0
- package/package.json +8 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
3
|
+
import { ensureCognitoToken, loadCachedTokens } from "../utils/cognito-session.js";
|
|
4
4
|
import { assertVaultApiRequestSupported, vaultApiFetch, } from "../utils/vault-api.js";
|
|
5
5
|
import { collectDiagnostics } from "../utils/feedback-diagnostics.js";
|
|
6
6
|
import { MAX_SCREENSHOTS, uploadScreenshots } from "../utils/feedback-screenshots.js";
|
|
@@ -8,6 +8,7 @@ import { FEEDBACK_LOGS_MAX_TOTAL_BYTES, FEEDBACK_LOGS_MIN_USEFUL_BYTES, collectF
|
|
|
8
8
|
import { uploadLogBundle } from "../utils/feedback-log-bundle.js";
|
|
9
9
|
import { resolveProcessFlagGate } from "../lib/flag-registry.js";
|
|
10
10
|
import { cachedLatestVersion } from "../utils/version-check.js";
|
|
11
|
+
import { registerFeedbackShowCommand } from "./feedback-show.js";
|
|
11
12
|
export const FEEDBACK_LATEST_VERSION_FLAG = "feedback.latest-version";
|
|
12
13
|
export const FEEDBACK_LATEST_VERSION_FLAG_FALLBACK = false;
|
|
13
14
|
export const FEEDBACK_REPORTER_KIND_FLAG = "feedback.reporter-kind";
|
|
@@ -199,7 +200,151 @@ export async function submitFeedback(opts) {
|
|
|
199
200
|
throw new Error(`Failed to submit feedback: ${errMsg}`);
|
|
200
201
|
}
|
|
201
202
|
const data = (await res.json());
|
|
202
|
-
return {
|
|
203
|
+
return {
|
|
204
|
+
id: data.id,
|
|
205
|
+
...(typeof data.publicUrl === "string" && data.publicUrl.length > 0
|
|
206
|
+
? { publicUrl: data.publicUrl }
|
|
207
|
+
: {}),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Public ticket pages: `hq feedback link <id>` and `hq feedback update <id>`
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
/** Customer-visible states accepted by `hq feedback update --state`. */
|
|
214
|
+
export const PUBLIC_TICKET_STATES = [
|
|
215
|
+
"triaged",
|
|
216
|
+
"in-progress",
|
|
217
|
+
"shipped",
|
|
218
|
+
"verified",
|
|
219
|
+
"resolved",
|
|
220
|
+
];
|
|
221
|
+
const FEEDBACK_ID_RE = /^feedback_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
222
|
+
export function assertFeedbackId(id) {
|
|
223
|
+
if (!FEEDBACK_ID_RE.test(id)) {
|
|
224
|
+
throw new Error(`Invalid feedback id '${id}': expected the full feedback_<uuid> id printed by hq feedback.`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The staff feedback routes gate on the id token's email claim, so send the
|
|
229
|
+
* cached id token when a person session has one. Machine identities already
|
|
230
|
+
* get their id token back from ensureCognitoToken.
|
|
231
|
+
*/
|
|
232
|
+
async function resolveStaffToken() {
|
|
233
|
+
const token = await ensureCognitoToken({ interactive: false });
|
|
234
|
+
const cached = loadCachedTokens();
|
|
235
|
+
return typeof cached?.idToken === "string" && cached.idToken.length > 0
|
|
236
|
+
? cached.idToken
|
|
237
|
+
: token;
|
|
238
|
+
}
|
|
239
|
+
async function readFeedbackError(res) {
|
|
240
|
+
const data = await res.json().catch(() => ({}));
|
|
241
|
+
return data &&
|
|
242
|
+
typeof data === "object" &&
|
|
243
|
+
!Array.isArray(data) &&
|
|
244
|
+
typeof data.error === "string"
|
|
245
|
+
? data.error
|
|
246
|
+
: res.statusText;
|
|
247
|
+
}
|
|
248
|
+
function publicUrlFrom(data) {
|
|
249
|
+
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
250
|
+
return null;
|
|
251
|
+
const url = data.publicUrl;
|
|
252
|
+
return typeof url === "string" && url.length > 0 ? url : null;
|
|
253
|
+
}
|
|
254
|
+
/** Resolve (and lazily mint) the public tracking link for a ticket. */
|
|
255
|
+
export async function fetchFeedbackLink(opts) {
|
|
256
|
+
const res = await vaultApiFetch({
|
|
257
|
+
token: opts.token,
|
|
258
|
+
path: `/v1/feedback/${opts.id}`,
|
|
259
|
+
method: "PATCH",
|
|
260
|
+
body: { ensurePublicToken: true },
|
|
261
|
+
});
|
|
262
|
+
if (!res.ok) {
|
|
263
|
+
throw new Error(`Failed to get the public link for ${opts.id}: ${await readFeedbackError(res)}`);
|
|
264
|
+
}
|
|
265
|
+
const url = publicUrlFrom(await res.json());
|
|
266
|
+
if (!url) {
|
|
267
|
+
throw new Error(`The server did not return a public link for ${opts.id}. Public ticket pages may not be deployed yet.`);
|
|
268
|
+
}
|
|
269
|
+
return url;
|
|
270
|
+
}
|
|
271
|
+
/** Post a customer-visible update; the server also mirrors it into #hq-feedback. */
|
|
272
|
+
export async function postFeedbackUpdate(opts) {
|
|
273
|
+
const res = await vaultApiFetch({
|
|
274
|
+
token: opts.token,
|
|
275
|
+
path: `/v1/feedback/${opts.id}`,
|
|
276
|
+
method: "PATCH",
|
|
277
|
+
body: { publicUpdate: { state: opts.state, note: opts.note } },
|
|
278
|
+
});
|
|
279
|
+
if (!res.ok) {
|
|
280
|
+
throw new Error(`Failed to update ${opts.id}: ${await readFeedbackError(res)}`);
|
|
281
|
+
}
|
|
282
|
+
const data = (await res.json());
|
|
283
|
+
return {
|
|
284
|
+
publicUrl: publicUrlFrom(data),
|
|
285
|
+
slack: typeof data.publicUpdateSlack === "string" ? data.publicUpdateSlack : null,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function registerLinkSubcommand(feedbackCmd) {
|
|
289
|
+
feedbackCmd
|
|
290
|
+
.command("link")
|
|
291
|
+
.description("Print the public tracking link for a ticket (status.hqforwork.com)")
|
|
292
|
+
.argument("<id>", "Feedback id (feedback_<uuid>)")
|
|
293
|
+
.action(async (id) => {
|
|
294
|
+
try {
|
|
295
|
+
assertFeedbackId(id);
|
|
296
|
+
const token = await resolveStaffToken();
|
|
297
|
+
assertVaultApiRequestSupported({ token, path: `/v1/feedback/${id}` });
|
|
298
|
+
const url = await fetchFeedbackLink({ id, token });
|
|
299
|
+
console.log(`Track: ${url}`);
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
function registerUpdateSubcommand(feedbackCmd) {
|
|
308
|
+
feedbackCmd
|
|
309
|
+
.command("update")
|
|
310
|
+
.description("Post a customer-visible status update on a ticket's public page and its #hq-feedback thread")
|
|
311
|
+
.argument("<id>", "Feedback id (feedback_<uuid>)")
|
|
312
|
+
.requiredOption("--state <state>", `One of: ${PUBLIC_TICKET_STATES.join(", ")}`)
|
|
313
|
+
.requiredOption("--note <text>", "What the customer should read (plain text, up to 2000 characters)")
|
|
314
|
+
.action(async (id, opts) => {
|
|
315
|
+
try {
|
|
316
|
+
assertFeedbackId(id);
|
|
317
|
+
if (!PUBLIC_TICKET_STATES.includes(opts.state)) {
|
|
318
|
+
throw new Error(`Invalid --state '${opts.state}': expected one of ${PUBLIC_TICKET_STATES.join(", ")}.`);
|
|
319
|
+
}
|
|
320
|
+
const note = opts.note.trim();
|
|
321
|
+
if (note.length === 0)
|
|
322
|
+
throw new Error("--note must not be empty.");
|
|
323
|
+
if (note.length > 2000)
|
|
324
|
+
throw new Error("--note must be at most 2000 characters.");
|
|
325
|
+
const token = await resolveStaffToken();
|
|
326
|
+
assertVaultApiRequestSupported({ token, path: `/v1/feedback/${id}` });
|
|
327
|
+
const result = await postFeedbackUpdate({
|
|
328
|
+
id,
|
|
329
|
+
token,
|
|
330
|
+
state: opts.state,
|
|
331
|
+
note,
|
|
332
|
+
});
|
|
333
|
+
console.log(`Updated: ${id} -> ${opts.state}`);
|
|
334
|
+
if (result.publicUrl)
|
|
335
|
+
console.log(`Track: ${result.publicUrl}`);
|
|
336
|
+
if (result.slack === "failed") {
|
|
337
|
+
console.error(chalk.yellow("Warning:"), "the update was saved but the #hq-feedback thread post failed.");
|
|
338
|
+
}
|
|
339
|
+
else if (result.slack === "skipped") {
|
|
340
|
+
console.error(chalk.yellow("Note:"), "the ticket has no #hq-feedback thread, so nothing was posted to Slack.");
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
203
348
|
}
|
|
204
349
|
function registerSubcommand(feedbackCmd, type) {
|
|
205
350
|
feedbackCmd
|
|
@@ -284,6 +429,8 @@ function registerSubcommand(feedbackCmd, type) {
|
|
|
284
429
|
logsEnabled,
|
|
285
430
|
});
|
|
286
431
|
console.log(`Submitted: ${result.id}`);
|
|
432
|
+
if (result.publicUrl)
|
|
433
|
+
console.log(`Track: ${result.publicUrl}`);
|
|
287
434
|
}
|
|
288
435
|
catch (err) {
|
|
289
436
|
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
@@ -297,5 +444,8 @@ export function registerFeedbackCommand(program) {
|
|
|
297
444
|
.description("Submit a bug report or feature request to HQ");
|
|
298
445
|
registerSubcommand(feedbackCmd, "bug");
|
|
299
446
|
registerSubcommand(feedbackCmd, "feature");
|
|
447
|
+
registerLinkSubcommand(feedbackCmd);
|
|
448
|
+
registerUpdateSubcommand(feedbackCmd);
|
|
449
|
+
registerFeedbackShowCommand(feedbackCmd);
|
|
300
450
|
}
|
|
301
451
|
//# sourceMappingURL=feedback.js.map
|
package/dist/commands/files.d.ts
CHANGED
|
@@ -9,6 +9,30 @@ export declare const FILES_SHARE_EMPTY_PREFIX_WARNING_FLAG = "files.share-empty-
|
|
|
9
9
|
export declare function parseDuration(input: string): number | null;
|
|
10
10
|
/** PRD upper bound on browser-launch share-session expiry. */
|
|
11
11
|
export declare const MAX_SHARE_SESSION_EXPIRY_MS: number;
|
|
12
|
+
/**
|
|
13
|
+
* A more specific ACL row under a prefix, as hq-pro reports it. File ACL
|
|
14
|
+
* resolution picks the single most specific row that covers a key, so each of
|
|
15
|
+
* these rows replaces the broader prefix's grants for the keys it covers
|
|
16
|
+
* (feedback_466fe8b8).
|
|
17
|
+
*/
|
|
18
|
+
export interface AclMaskingRow {
|
|
19
|
+
path: string;
|
|
20
|
+
open: boolean;
|
|
21
|
+
creatorUid: string;
|
|
22
|
+
grantees: Array<{
|
|
23
|
+
granteeType: string;
|
|
24
|
+
granteeId: string;
|
|
25
|
+
permission: string;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/** The warning `hq files share` prints when a grant is shadowed by more specific rows. */
|
|
29
|
+
export declare function formatShadowedGrantWarning(input: {
|
|
30
|
+
prefix: string;
|
|
31
|
+
principal: string;
|
|
32
|
+
permission: string;
|
|
33
|
+
companySlug?: string;
|
|
34
|
+
maskedBy: AclMaskingRow[];
|
|
35
|
+
}): string;
|
|
12
36
|
export interface ShareSessionResponse {
|
|
13
37
|
url: string;
|
|
14
38
|
token?: string;
|
package/dist/commands/files.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
+
import { aclLockedBy, describeAclLock } from "./acl-lock.js";
|
|
2
3
|
import open from "open";
|
|
3
4
|
import * as readline from "node:readline";
|
|
4
5
|
import { resolveVaultCredential } from "../utils/resolve-vault-credential.js";
|
|
@@ -9,6 +10,7 @@ import { looksLikeCompanyUid } from "../utils/vault-api.js";
|
|
|
9
10
|
import { AuthError } from "../utils/auth-error.js";
|
|
10
11
|
import { formatAccessDenied, offerAccessLadder, } from "../utils/access-denied-hint.js";
|
|
11
12
|
import { fetchChannels, matchChannelsByName } from "./dm.js";
|
|
13
|
+
import { suggestChannels, withChannelSuggestions } from "./channel-suggest.js";
|
|
12
14
|
import { resolveCompanySlug, resolvePersonWithRosterFallback, } from "./people.js";
|
|
13
15
|
import { findHqRoot } from "../utils/manifest.js";
|
|
14
16
|
import { maxLength } from "../utils/table-width.js";
|
|
@@ -55,6 +57,39 @@ function privateFolderExplanation(prefix) {
|
|
|
55
57
|
return (`Private folder '${prefix}' — write can create only a new direct child. ` +
|
|
56
58
|
"Children are creator-only; this grant does not read, list, overwrite, or delete them.");
|
|
57
59
|
}
|
|
60
|
+
function describeMaskingGrantees(row) {
|
|
61
|
+
if (row.open)
|
|
62
|
+
return "open (all members read)";
|
|
63
|
+
if (row.grantees.length === 0)
|
|
64
|
+
return "none (creator only)";
|
|
65
|
+
return row.grantees
|
|
66
|
+
.map((g) => g.granteeType === "company-wide"
|
|
67
|
+
? `everyone:${g.permission}`
|
|
68
|
+
: `${g.granteeId}:${g.permission}`)
|
|
69
|
+
.join(", ");
|
|
70
|
+
}
|
|
71
|
+
function printMaskingTable(rows) {
|
|
72
|
+
const PATH_W = Math.max(4, ...rows.map((r) => displayAclPattern(r.path).length));
|
|
73
|
+
const CREATOR_W = Math.max(7, ...rows.map((r) => r.creatorUid.length));
|
|
74
|
+
console.log(chalk.bold(["PATH".padEnd(PATH_W), "CREATOR".padEnd(CREATOR_W), "GRANTS"].join(" ")));
|
|
75
|
+
for (const row of rows) {
|
|
76
|
+
console.log([
|
|
77
|
+
displayAclPattern(row.path).padEnd(PATH_W),
|
|
78
|
+
row.creatorUid.padEnd(CREATOR_W),
|
|
79
|
+
describeMaskingGrantees(row),
|
|
80
|
+
].join(" "));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** The warning `hq files share` prints when a grant is shadowed by more specific rows. */
|
|
84
|
+
export function formatShadowedGrantWarning(input) {
|
|
85
|
+
const paths = input.maskedBy.map((row) => `'${row.path}'`).join(", ");
|
|
86
|
+
const company = input.companySlug ? ` --company ${input.companySlug}` : "";
|
|
87
|
+
return (`Warning: this grant takes no effect under ${input.maskedBy.length} more specific rule(s) ` +
|
|
88
|
+
`that do not include ${input.principal}: ${paths}. ` +
|
|
89
|
+
"Access under those paths is unchanged (the most specific rule wins). " +
|
|
90
|
+
`Share them too, for example: hq files share '${input.maskedBy[0]?.path ?? "<path>"}' --with ${input.principal} --permission ${input.permission}${company}` +
|
|
91
|
+
" — or remove the rule with hq files unshare.");
|
|
92
|
+
}
|
|
58
93
|
function formatAclPatternConflict(prefix, serverMessage) {
|
|
59
94
|
const privatePattern = prefix.endsWith("/*")
|
|
60
95
|
? prefix.slice(0, -1)
|
|
@@ -455,17 +490,21 @@ export function registerFilesCommand(program) {
|
|
|
455
490
|
throw new Error('Invalid ACL tree response');
|
|
456
491
|
}
|
|
457
492
|
const row = tree.directRow;
|
|
493
|
+
// The most-specific row has no grants, so broader grants do not apply.
|
|
494
|
+
const lockedBy = aclLockedBy(tree);
|
|
458
495
|
if (opts.json) {
|
|
459
|
-
console.log(JSON.stringify({ ...tree, schemaVersion: 1, companyUid, exists: row !== null }, null, 2));
|
|
496
|
+
console.log(JSON.stringify({ ...tree, schemaVersion: 1, companyUid, exists: row !== null, lockedBy }, null, 2));
|
|
460
497
|
return;
|
|
461
498
|
}
|
|
462
499
|
// No own row AND nothing inherited or granted below — preserve the
|
|
463
500
|
// original "no ACL record" exit path.
|
|
464
|
-
if (!row && tree.inherited.length === 0 && tree.children.length === 0) {
|
|
501
|
+
if (!row && !lockedBy && tree.inherited.length === 0 && tree.children.length === 0) {
|
|
465
502
|
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
466
503
|
process.exit(1);
|
|
467
504
|
}
|
|
468
|
-
const aclStatus =
|
|
505
|
+
const aclStatus = lockedBy
|
|
506
|
+
? lockedBy.readUnknown ? "locked unless open" : "locked"
|
|
507
|
+
: row?.open ? "open" : "restricted";
|
|
469
508
|
console.log(chalk.green(`ACL for ${displayAclPattern(tree.prefix)} (${aclStatus})`));
|
|
470
509
|
if (isPrivateFolderPattern(tree.prefix)) {
|
|
471
510
|
console.log(chalk.gray(privateFolderExplanation(tree.prefix)));
|
|
@@ -473,9 +512,12 @@ export function registerFilesCommand(program) {
|
|
|
473
512
|
if (row) {
|
|
474
513
|
console.log(`Creator: ${row.creatorUid}`);
|
|
475
514
|
}
|
|
476
|
-
else {
|
|
515
|
+
else if (!lockedBy) {
|
|
477
516
|
console.log(chalk.gray("No direct ACL row — access flows from the inherited/descendant grants below."));
|
|
478
517
|
}
|
|
518
|
+
if (lockedBy) {
|
|
519
|
+
console.log(chalk.yellow(describeAclLock(lockedBy, displayAclPattern(lockedBy.path))));
|
|
520
|
+
}
|
|
479
521
|
if (tree.effectivePermission) {
|
|
480
522
|
console.log(`Your effective permission: ${tree.effectivePermission}`);
|
|
481
523
|
}
|
|
@@ -526,9 +568,9 @@ export function registerFilesCommand(program) {
|
|
|
526
568
|
const directEntries = tree.direct;
|
|
527
569
|
if (directEntries.length === 0) {
|
|
528
570
|
if (row?.open) {
|
|
529
|
-
console.log(chalk.gray("
|
|
571
|
+
console.log(chalk.gray("No grants on this row (open: broader grants still apply). All active members have at least read access."));
|
|
530
572
|
}
|
|
531
|
-
else if (row) {
|
|
573
|
+
else if (row && !lockedBy) {
|
|
532
574
|
console.log(chalk.gray("No explicit grants on this prefix — only creator has access."));
|
|
533
575
|
}
|
|
534
576
|
}
|
|
@@ -537,13 +579,25 @@ export function registerFilesCommand(program) {
|
|
|
537
579
|
printEntryTable(directEntries, false);
|
|
538
580
|
}
|
|
539
581
|
if (tree.inherited.length > 0) {
|
|
540
|
-
console.log(
|
|
582
|
+
console.log(lockedBy
|
|
583
|
+
? lockedBy.readUnknown
|
|
584
|
+
? `\nGranted on ancestor prefixes (apply only if ${displayAclPattern(lockedBy.path)} is open):`
|
|
585
|
+
: `\nGranted on ancestor prefixes (do not apply: locked by ${displayAclPattern(lockedBy.path)}):`
|
|
586
|
+
: "\nInherited (granted on an ancestor prefix):");
|
|
541
587
|
printEntryTable(tree.inherited, true);
|
|
542
588
|
}
|
|
543
589
|
if (tree.children.length > 0) {
|
|
544
590
|
console.log("\nGranted on descendant prefixes (do not affect this prefix's access):");
|
|
545
591
|
printEntryTable(tree.children, true);
|
|
546
592
|
}
|
|
593
|
+
if (Array.isArray(tree.masking) && tree.masking.length > 0) {
|
|
594
|
+
console.log(`\nMore specific rules under ${displayAclPattern(tree.prefix)} (each replaces this prefix's grants for its own paths):`);
|
|
595
|
+
printMaskingTable(tree.masking);
|
|
596
|
+
if (tree.maskingTruncated) {
|
|
597
|
+
console.log(chalk.gray(" ...list truncated; use --json for the first page."));
|
|
598
|
+
}
|
|
599
|
+
console.log(chalk.gray(`A grant on ${displayAclPattern(tree.prefix)} does not reach these paths unless they grant the same principal. Share each path too, or remove its rule with hq files unshare.`));
|
|
600
|
+
}
|
|
547
601
|
const allEntries = [...tree.direct, ...tree.inherited, ...tree.children];
|
|
548
602
|
if (allEntries.some((entry) => entry.granteeType === "company-wide")) {
|
|
549
603
|
console.log(chalk.gray("\nCompany-wide grants apply to every active member, including guests. Demoting someone to guest does not exclude them. Use hq members remove to end membership, or hq access <path> --as <email> to see effective access."));
|
|
@@ -704,7 +758,7 @@ async function resolveShareChannelTarget(params) {
|
|
|
704
758
|
const channels = await fetchChannels(token);
|
|
705
759
|
const matches = matchChannelsByName(channels, params.channelName);
|
|
706
760
|
if (matches.length === 0) {
|
|
707
|
-
console.error(chalk.red(`No channel named '${params.channelName}' — run \`hq channels\` to see your channels
|
|
761
|
+
console.error(chalk.red(withChannelSuggestions(`No channel named '${params.channelName}' — run \`hq channels\` to see your channels.`, suggestChannels(channels, params.channelName))));
|
|
708
762
|
process.exit(1);
|
|
709
763
|
}
|
|
710
764
|
if (matches.length > 1) {
|
|
@@ -894,6 +948,15 @@ async function runDirectGrant(params) {
|
|
|
894
948
|
if (isPrivateFolderPattern(printedPrefix)) {
|
|
895
949
|
console.log(chalk.gray(privateFolderExplanation(printedPrefix)));
|
|
896
950
|
}
|
|
951
|
+
if (Array.isArray(data.maskedBy) && data.maskedBy.length > 0) {
|
|
952
|
+
console.warn(chalk.yellow(formatShadowedGrantWarning({
|
|
953
|
+
prefix: printedPrefix,
|
|
954
|
+
principal: isAll ? "@all" : principal,
|
|
955
|
+
permission: params.permission,
|
|
956
|
+
...(params.companySlug ? { companySlug: params.companySlug } : {}),
|
|
957
|
+
maskedBy: data.maskedBy,
|
|
958
|
+
})));
|
|
959
|
+
}
|
|
897
960
|
if (typeof data.record?.error === "string" &&
|
|
898
961
|
typeof data.record.status === "number" &&
|
|
899
962
|
data.record.status >= 400) {
|
|
@@ -548,12 +548,16 @@ async function connectViaOAuth(token, companyUid, target, opts) {
|
|
|
548
548
|
...target.ref,
|
|
549
549
|
...(requestedScopes ? { scopes: requestedScopes } : {}),
|
|
550
550
|
};
|
|
551
|
-
|
|
552
|
-
|
|
551
|
+
const userClient = await resolveUserOAuthClient(opts, target.label);
|
|
552
|
+
if (opts.linkOnly) {
|
|
553
|
+
return await linkOnlySignIn(token, companyUid, target, opts, {
|
|
554
|
+
...startInput,
|
|
555
|
+
...(userClient ?? {}),
|
|
556
|
+
});
|
|
557
|
+
}
|
|
553
558
|
// A user-registered app pins the console callback and skips the loopback
|
|
554
559
|
// entirely — see `userClientSignIn` for why an ephemeral port cannot be the
|
|
555
560
|
// redirect URI here.
|
|
556
|
-
const userClient = await resolveUserOAuthClient(opts, target.label);
|
|
557
561
|
if (userClient)
|
|
558
562
|
return await userClientSignIn(token, companyUid, target, opts, userClient, requestedScopes);
|
|
559
563
|
// A loopback listener only works if the browser runs on THIS machine. Over
|
|
@@ -9,9 +9,11 @@ import { assertUniqueAccountHomes, providerCompactAfterMs } from "../lib/lanes/c
|
|
|
9
9
|
import { resolveLaneEffort } from "../lib/lanes/effort.js";
|
|
10
10
|
import { LanesError } from "../lib/lanes/errors.js";
|
|
11
11
|
import { CAPACITY_ONLY_REASON } from "../lib/capacity/accounts.js";
|
|
12
|
+
import { claudeOpenCommand } from "../lib/capacity/ledger.js";
|
|
12
13
|
import { accountQuotaView, clearAccountQuota, loadQuotaState } from "../lib/lanes/quota.js";
|
|
13
14
|
import { resolveLaneModel } from "../lib/lanes/spawn.js";
|
|
14
15
|
import { leadLaneIds } from "../lib/lanes/admission.js";
|
|
16
|
+
import { resolveLaneCompany } from "../lib/lanes/lane-company.js";
|
|
15
17
|
export function createLaneAccountsHandlers(deps) {
|
|
16
18
|
const { admitQueuedFromCtx, automaticBalanceMoveHistoryReason, ensureDir, laneWithRecoveredProviderSession, loadClaudeBalanceHostSnapshots, parseRemoteJson, prepareAccountMove, printJson, reconcileHostFromCtx, resolveCallerPath } = deps;
|
|
17
19
|
const VENDOR_LOGIN = {
|
|
@@ -124,6 +126,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
124
126
|
hostname: ctx.hostname(),
|
|
125
127
|
homedir: ctx.homedir,
|
|
126
128
|
probe: ctx.probeProvider,
|
|
129
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
127
130
|
env: ctx.env,
|
|
128
131
|
}).map((status) => {
|
|
129
132
|
const accountCfg = config.providers[provider].accounts[status.id];
|
|
@@ -146,6 +149,10 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
146
149
|
error: "home_missing",
|
|
147
150
|
fix_command: `hq lanes accounts set-home ${provider} ${status.id} <host-home>`,
|
|
148
151
|
} : {}),
|
|
152
|
+
...(provider === "claude" && status.sign_in_state === "expired" ? {
|
|
153
|
+
error: "sign_in_expired",
|
|
154
|
+
fix_command: `${claudeOpenCommand(status.home, ctx.homedir())} then run /login`,
|
|
155
|
+
} : {}),
|
|
149
156
|
...(provider === "claude" ? {
|
|
150
157
|
identity: status.identity ?? null,
|
|
151
158
|
credential_matches_interactive: differs === null ? null : !differs,
|
|
@@ -192,7 +199,11 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
192
199
|
const balanceGateEnabled = true;
|
|
193
200
|
const recentUsageEnabled = await deps.readClaudeAccountBalanceSignalsEnabled(ctx);
|
|
194
201
|
const loads = accountLoadsForReport({ hosts: loaded.snapshots, config, quotaState });
|
|
195
|
-
const suggestions = planClaudeBalanceMoves({
|
|
202
|
+
const suggestions = planClaudeBalanceMoves({
|
|
203
|
+
hosts: loaded.snapshots,
|
|
204
|
+
config,
|
|
205
|
+
laneCompany: (lane) => resolveLaneCompany(hqRoot, lane).company,
|
|
206
|
+
});
|
|
196
207
|
const selections = loaded.snapshots.map((host) => {
|
|
197
208
|
try {
|
|
198
209
|
const selection = selectClaudeAccount({
|
|
@@ -428,6 +439,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
428
439
|
existsSync: ctx.existsSync,
|
|
429
440
|
hostname,
|
|
430
441
|
probe: ctx.probeProvider,
|
|
442
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
431
443
|
env: ctx.env,
|
|
432
444
|
});
|
|
433
445
|
if (status.logged_in !== true) {
|
|
@@ -567,6 +579,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
567
579
|
existsSync: ctx.existsSync,
|
|
568
580
|
hostname: ctx.hostname(),
|
|
569
581
|
probe: ctx.probeProvider,
|
|
582
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
570
583
|
env: ctx.env,
|
|
571
584
|
});
|
|
572
585
|
if (status.logged_in !== true) {
|
|
@@ -584,6 +597,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
584
597
|
existsSync: ctx.existsSync,
|
|
585
598
|
hostname: ctx.hostname(),
|
|
586
599
|
probe: ctx.probeProvider,
|
|
600
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
587
601
|
env: ctx.env,
|
|
588
602
|
}),
|
|
589
603
|
});
|
|
@@ -613,6 +627,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
613
627
|
existsSync: ctx.existsSync,
|
|
614
628
|
hostname: ctx.hostname(),
|
|
615
629
|
probe: ctx.probeProvider,
|
|
630
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
616
631
|
env: ctx.env,
|
|
617
632
|
}),
|
|
618
633
|
});
|
|
@@ -101,7 +101,16 @@ export async function runLaneLoopController(ctx, laneId, deps) {
|
|
|
101
101
|
execute: async ({ envelope, signal, fresh, sessionId, repairError }) => {
|
|
102
102
|
let current = readLane(hqRoot, laneId);
|
|
103
103
|
const data = envelope;
|
|
104
|
-
const
|
|
104
|
+
const envelopeStory = typeof data.story_id === "string" && data.story_id.trim() ? data.story_id.trim() : undefined;
|
|
105
|
+
// A lane created with --no-story keeps no_story_reason and an empty
|
|
106
|
+
// story_id; the record schema rejects both together. Ignore an
|
|
107
|
+
// envelope story on such a lane instead of writing a record that can
|
|
108
|
+
// no longer be loaded or cancelled.
|
|
109
|
+
const ignoreEnvelopeStory = envelopeStory !== undefined && !current.story_id && !!current.no_story_reason;
|
|
110
|
+
if (ignoreEnvelopeStory) {
|
|
111
|
+
process.stderr.write(`hq lanes: ignoring envelope story_id ${envelopeStory} for no-story lane ${laneId}\n`);
|
|
112
|
+
}
|
|
113
|
+
const storyId = envelopeStory !== undefined && !ignoreEnvelopeStory ? envelopeStory : current.story_id;
|
|
105
114
|
if (storyId !== current.story_id) {
|
|
106
115
|
current = retryUpdateLane(hqRoot, laneId, "command", (record) => {
|
|
107
116
|
const stories = laneStories(record);
|
|
@@ -153,8 +162,8 @@ export async function runLaneLoopController(ctx, laneId, deps) {
|
|
|
153
162
|
fs.writeFileSync(briefPath, [
|
|
154
163
|
"Follow the lane worker instructions and complete this single bounded phase.",
|
|
155
164
|
handoffRepairEnabled
|
|
156
|
-
? "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts. The lane envelope decision uses done/ask/blocked; handoff status uses passed/failed/blocked. Allowed handoff status values are passed, failed, blocked. Allowed back_pressure values for tests, lint, typecheck, and build are pass, fail, skip. Include story_id, phase, worker_id, summary, files_changed, commits, back_pressure, and context_for_next."
|
|
157
|
-
: "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts; it must identify the requested story_id and phase and include worker_id, status, summary, files_changed, commits, back_pressure (tests/lint/typecheck/build), and context_for_next.",
|
|
165
|
+
? "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts. The lane envelope decision uses done/ask/blocked; handoff status uses passed/failed/blocked. Allowed handoff status values are passed, failed, blocked. Allowed back_pressure values for tests, lint, typecheck, and build are pass, fail, skip. Include story_id, phase, worker_id, summary, files_changed, commits, back_pressure, and context_for_next. summary, context_for_next, and each ac_evidence evidence are single strings, never arrays. Copy each acceptance criterion verbatim into ac_evidence[].criterion; keep its 0-based index and do not shorten or paraphrase the text."
|
|
166
|
+
: "Return the standard hq-lane-envelope.v1 envelope when the phase is complete. For a section 8 phase envelope, put a complete hq-phase-handoff/v1 object in artifacts; it must identify the requested story_id and phase and include worker_id, status, summary, files_changed, commits, back_pressure (tests/lint/typecheck/build), and context_for_next. summary, context_for_next, and each ac_evidence evidence are single strings, never arrays. Copy each acceptance criterion verbatim into ac_evidence[].criterion; keep its 0-based index and do not shorten or paraphrase the text.",
|
|
158
167
|
repairInstructions,
|
|
159
168
|
"",
|
|
160
169
|
prompt,
|
|
@@ -173,6 +182,7 @@ export async function runLaneLoopController(ctx, laneId, deps) {
|
|
|
173
182
|
}
|
|
174
183
|
await spawnLaneWorker(hqRoot, current, {
|
|
175
184
|
env: ctx.env,
|
|
185
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
176
186
|
modelDeps: { readFileSync: ctx.readFileSync, probe: ctx.probeProvider, env: ctx.env },
|
|
177
187
|
existsSync: ctx.existsSync,
|
|
178
188
|
spawnProcess: ctx.spawnProcess,
|
|
@@ -201,7 +211,7 @@ export async function runLaneLoopController(ctx, laneId, deps) {
|
|
|
201
211
|
await deps.resumeLane(laneId, {
|
|
202
212
|
text: `Read ${briefPath} and follow it as the complete instruction for this phase.`,
|
|
203
213
|
project: current.project_id,
|
|
204
|
-
story: storyId,
|
|
214
|
+
...(storyId ? { story: storyId } : {}),
|
|
205
215
|
freshThread: fresh,
|
|
206
216
|
...(sessionId ? { loopSessionId: sessionId } : {}),
|
|
207
217
|
...(repairError !== undefined ? { loopHandoffRepair: true } : {}),
|
|
@@ -560,16 +560,17 @@ export function createLaneMessageHandlers(deps) {
|
|
|
560
560
|
? err.details.account_reasons.filter((reason) => reason !== null && typeof reason === "object" && !Array.isArray(reason))
|
|
561
561
|
: [];
|
|
562
562
|
const held = reasons.filter((reason) => reason.reason === "quota_held");
|
|
563
|
-
|
|
563
|
+
const companyUnavailable = reasons.some((reason) => reason.reason === "company_mismatch");
|
|
564
|
+
if (held.length === 0 && !companyUnavailable)
|
|
564
565
|
throw err;
|
|
565
566
|
const resetTimes = held
|
|
566
567
|
.map((reason) => reason.quota_reset_at)
|
|
567
568
|
.filter((value) => typeof value === "string" && Number.isFinite(Date.parse(value)))
|
|
568
569
|
.sort((a, b) => Date.parse(a) - Date.parse(b));
|
|
569
|
-
if (resetTimes.length === 0)
|
|
570
|
+
if (resetTimes.length === 0 && !companyUnavailable)
|
|
570
571
|
throw err;
|
|
571
572
|
quotaHoldDeferred = {
|
|
572
|
-
quota_reset_at: resetTimes[0],
|
|
573
|
+
...(resetTimes[0] !== undefined ? { quota_reset_at: resetTimes[0] } : {}),
|
|
573
574
|
...(lane.model !== undefined ? { model: lane.model } : {}),
|
|
574
575
|
};
|
|
575
576
|
automaticAccountSelection = undefined;
|
|
@@ -911,6 +912,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
911
912
|
}
|
|
912
913
|
const started = spawnLaneWorker(hqRoot, lane, {
|
|
913
914
|
env: ctx.env,
|
|
915
|
+
readClaudeCredential: ctx.readClaudeCredential,
|
|
914
916
|
modelDeps: {
|
|
915
917
|
readFileSync: ctx.readFileSync,
|
|
916
918
|
probe: ctx.probeProvider,
|
|
@@ -974,7 +976,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
974
976
|
const deliveryStatus = move?.queued
|
|
975
977
|
? "queued_on_target_account"
|
|
976
978
|
: queuedOnQuotaHold
|
|
977
|
-
? "queued_on_quota_hold"
|
|
979
|
+
? quotaHoldDeferred?.quota_reset_at === undefined ? "queued_on_account" : "queued_on_quota_hold"
|
|
978
980
|
: queuedOnAdmission
|
|
979
981
|
? "queued_on_admission"
|
|
980
982
|
: mechanism === "dropbox"
|