@indigoai-us/hq-cli 5.309.1 → 5.311.0
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 +18 -0
- package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
- package/assets/scaffold/core/scripts/jobs-validate.sh +1 -1
- package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
- package/dist/command-catalog.generated.d.ts +28 -2
- package/dist/command-catalog.generated.js +34 -2
- package/dist/commands/channels.d.ts +1 -9
- package/dist/commands/channels.js +2 -2
- package/dist/commands/company-transfer.d.ts +4 -28
- package/dist/commands/company-transfer.js +5 -49
- package/dist/commands/core-checkpoint-stop-gate-native.d.ts +34 -0
- package/dist/commands/core-checkpoint-stop-gate-native.js +713 -0
- package/dist/commands/core-checkpoint.d.ts +0 -24
- package/dist/commands/core-checkpoint.js +4 -4
- package/dist/commands/core-command-catalog.js +1 -1
- package/dist/commands/core-dm-bind-native.d.ts +32 -0
- package/dist/commands/core-dm-bind-native.js +668 -0
- package/dist/commands/core-hq-session-native.d.ts +39 -0
- package/dist/commands/core-hq-session-native.js +579 -0
- package/dist/commands/core-jobs-native.js +1 -4
- package/dist/commands/core-native-session-flag.d.ts +4 -0
- package/dist/commands/core-native-session-flag.js +7 -0
- package/dist/commands/core-native-utilities.d.ts +2 -1
- package/dist/commands/core-native-utilities.js +4 -2
- package/dist/commands/core-native-worker-skills-flag.d.ts +4 -0
- package/dist/commands/core-native-worker-skills-flag.js +7 -0
- package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
- package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
- package/dist/commands/core-worker-lint-native.d.ts +4 -0
- package/dist/commands/core-worker-lint-native.js +160 -0
- package/dist/commands/core-worker-share-native.d.ts +5 -0
- package/dist/commands/core-worker-share-native.js +260 -0
- package/dist/commands/core-worker-skills-command.d.ts +8 -0
- package/dist/commands/core-worker-skills-command.js +33 -0
- package/dist/commands/core-worker-skills-runtime.d.ts +33 -0
- package/dist/commands/core-worker-skills-runtime.js +86 -0
- package/dist/commands/core.js +10 -2
- package/dist/commands/dm.d.ts +3 -7
- package/dist/commands/dm.js +3 -3
- package/dist/commands/integrations-import.d.ts +1 -1
- package/dist/commands/integrations-import.js +34 -19
- package/dist/commands/lanes-accounts.js +3 -1
- package/dist/commands/lanes-message.js +84 -10
- package/dist/commands/lanes-shared.d.ts +4 -1
- package/dist/commands/lanes.d.ts +4 -1
- package/dist/commands/lanes.js +48 -7
- package/dist/commands/personal-connector-settings-flag.d.ts +4 -0
- package/dist/commands/personal-connector-settings-flag.js +7 -0
- package/dist/commands/report.d.ts +1 -26
- package/dist/commands/report.js +6 -6
- package/dist/fast-core.js +15 -2
- package/dist/fast-scaffold.js +25 -2
- package/dist/index.js +8 -0
- package/dist/lib/bot/continuity.d.ts +2 -1
- package/dist/lib/bot/job-completions.d.ts +0 -2
- package/dist/lib/bot/job-completions.js +2 -2
- package/dist/lib/bot/local-config.d.ts +0 -10
- package/dist/lib/bot/local-config.js +3 -9
- package/dist/lib/bot/owner-context.d.ts +2 -6
- package/dist/lib/bot/owner-context.js +3 -3
- package/dist/lib/bot/runtime/windows.d.ts +0 -2
- package/dist/lib/bot/runtime/windows.js +1 -1
- package/dist/lib/bot/setup-explain.d.ts +0 -4
- package/dist/lib/bot/setup-explain.js +2 -2
- package/dist/lib/daemon/catalog.d.ts +3 -2
- package/dist/lib/daemon/hq-cloud.d.ts +2 -2
- package/dist/lib/daemon/hq-cloud.js +1 -1
- package/dist/lib/daemon/machine-type.d.ts +3 -4
- package/dist/lib/daemon/machine-type.js +2 -2
- package/dist/lib/daemon/remote-control.d.ts +0 -1
- package/dist/lib/daemon/remote-control.js +1 -1
- package/dist/lib/daemon/supervisor.d.ts +4 -9
- package/dist/lib/daemon/supervisor.js +5 -5
- package/dist/lib/lanes/admission-host.d.ts +3 -0
- package/dist/lib/lanes/admission-host.js +4 -0
- package/dist/lib/lanes/autopilot-registry.d.ts +62 -0
- package/dist/lib/lanes/autopilot-registry.js +192 -0
- package/dist/lib/lanes/autopilot.d.ts +2 -0
- package/dist/lib/lanes/autopilot.js +18 -5
- package/dist/lib/lanes/claude-balance.d.ts +6 -0
- package/dist/lib/lanes/claude-balance.js +17 -2
- package/dist/lib/lanes/keep-alive.d.ts +3 -0
- package/dist/lib/lanes/keep-alive.js +3 -0
- package/dist/lib/lanes/reconcile.js +14 -6
- package/dist/lib/lanes/spawn.js +2 -0
- package/dist/lib/mesh/live/daemon/credentials.d.ts +2 -17
- package/dist/lib/mesh/live/daemon/credentials.js +9 -9
- package/dist/lib/mesh/live/daemon/index.d.ts +5 -5
- package/dist/lib/mesh/live/daemon/index.js +2 -2
- package/dist/lib/mesh/live/daemon/state.d.ts +1 -1
- package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +4 -11
- package/dist/lib/mesh/live/daemon/transcript-watch.js +5 -5
- package/dist/lib/work-context/company.d.ts +2 -25
- package/dist/lib/work-context/company.js +3 -3
- package/dist/lib/work-context/config.d.ts +3 -8
- package/dist/lib/work-context/config.js +2 -13
- package/dist/lib/work-context/state.d.ts +4 -13
- package/dist/lib/work-context/state.js +2 -30
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
2
|
+
import { coreNativeWorkerSkillsEnabled } from "./core-native-worker-skills-flag.js";
|
|
3
|
+
import { runWorkerSkillLint, WORKER_SKILL_LINT_ASSET } from "./core-worker-lint-native.js";
|
|
4
|
+
import { runWorkerSkillShare, WORKER_SKILL_SHARE_ASSET } from "./core-worker-share-native.js";
|
|
5
|
+
const assets = {
|
|
6
|
+
lint: WORKER_SKILL_LINT_ASSET,
|
|
7
|
+
share: WORKER_SKILL_SHARE_ASSET,
|
|
8
|
+
};
|
|
9
|
+
/** Keep the shell rollback route byte-transparent while the family flag is off. */
|
|
10
|
+
export async function runWorkerSkillCommand(name, args, context) {
|
|
11
|
+
if (!(await coreNativeWorkerSkillsEnabled())) {
|
|
12
|
+
const result = runBundledScript({
|
|
13
|
+
asset: assets[name],
|
|
14
|
+
args,
|
|
15
|
+
cwd: context.cwd,
|
|
16
|
+
hqRoot: context.hqRoot,
|
|
17
|
+
});
|
|
18
|
+
if (result.code !== 0)
|
|
19
|
+
process.exit(result.code);
|
|
20
|
+
return result.code;
|
|
21
|
+
}
|
|
22
|
+
const result = name === "lint"
|
|
23
|
+
? runWorkerSkillLint(args, { cwd: context.cwd })
|
|
24
|
+
: runWorkerSkillShare(args, { cwd: context.cwd });
|
|
25
|
+
if (result.stdout)
|
|
26
|
+
process.stdout.write(result.stdout);
|
|
27
|
+
if (result.stderr)
|
|
28
|
+
process.stderr.write(result.stderr);
|
|
29
|
+
if (result.code !== 0)
|
|
30
|
+
process.exit(result.code);
|
|
31
|
+
return result.code;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=core-worker-skills-command.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
export type WorkerSkillResult = {
|
|
3
|
+
code: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
};
|
|
7
|
+
export type WorkerSkillFs = Pick<typeof fs, "lstatSync" | "statSync" | "readdirSync" | "readFileSync" | "readlinkSync" | "existsSync" | "mkdirSync" | "renameSync" | "unlinkSync" | "symlinkSync">;
|
|
8
|
+
export type GitResult = {
|
|
9
|
+
code: number;
|
|
10
|
+
stdout: string;
|
|
11
|
+
};
|
|
12
|
+
export type GitRunner = (args: string[], cwd: string, env: NodeJS.ProcessEnv) => GitResult;
|
|
13
|
+
export type WorkerSkillDependencies = {
|
|
14
|
+
cwd: string;
|
|
15
|
+
env?: NodeJS.ProcessEnv;
|
|
16
|
+
fs?: WorkerSkillFs;
|
|
17
|
+
runGit?: GitRunner;
|
|
18
|
+
/** The bundled shell's absolute $0, used for byte-identical usage text. */
|
|
19
|
+
scriptPath?: string;
|
|
20
|
+
};
|
|
21
|
+
export declare function runGit(args: string[], cwd: string, env: NodeJS.ProcessEnv, injected?: GitRunner): GitResult;
|
|
22
|
+
/** Match `git rev-parse --show-toplevel || pwd` without consulting HQ_ROOT. */
|
|
23
|
+
export declare function resolveWorkerCallerRoot(dependencies: WorkerSkillDependencies): {
|
|
24
|
+
root: string;
|
|
25
|
+
env: NodeJS.ProcessEnv;
|
|
26
|
+
fs: WorkerSkillFs;
|
|
27
|
+
};
|
|
28
|
+
export declare function errorCode(error: unknown): string | undefined;
|
|
29
|
+
export declare function missingOrUnreadable(error: unknown): boolean;
|
|
30
|
+
export declare function isDirectory(filePath: string, fileSystem: WorkerSkillFs): boolean;
|
|
31
|
+
export declare function isSymlink(filePath: string, fileSystem: WorkerSkillFs): boolean;
|
|
32
|
+
export declare function existsFollowingLinks(filePath: string, fileSystem: WorkerSkillFs): boolean;
|
|
33
|
+
//# sourceMappingURL=core-worker-skills-runtime.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
const SYSTEM_FS = fs;
|
|
4
|
+
function processGit(args, cwd, env) {
|
|
5
|
+
// Tests that set this marker must inject a runner. This guard catches an
|
|
6
|
+
// accidental escape from a synthetic fixture before any real executable can
|
|
7
|
+
// be reached through PATH.
|
|
8
|
+
if (env.HQ_CORE_STUBS === "1") {
|
|
9
|
+
throw new Error("real git execution is disabled while HQ_CORE_STUBS=1");
|
|
10
|
+
}
|
|
11
|
+
const result = spawnSync("git", args, { cwd, env, encoding: "utf8" });
|
|
12
|
+
return {
|
|
13
|
+
code: result.status ?? 127,
|
|
14
|
+
stdout: result.stdout ?? "",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export function runGit(args, cwd, env, injected) {
|
|
18
|
+
return (injected ?? processGit)(args, cwd, env);
|
|
19
|
+
}
|
|
20
|
+
function shellPwd(cwd, env, fileSystem) {
|
|
21
|
+
const logical = env.PWD;
|
|
22
|
+
if (!logical || !logical.startsWith("/"))
|
|
23
|
+
return cwd;
|
|
24
|
+
try {
|
|
25
|
+
const logicalStat = fileSystem.statSync(logical);
|
|
26
|
+
const cwdStat = fileSystem.statSync(cwd);
|
|
27
|
+
if (logicalStat.dev === cwdStat.dev && logicalStat.ino === cwdStat.ino)
|
|
28
|
+
return logical;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Bash falls back to its physical cwd when PWD no longer names this inode.
|
|
32
|
+
}
|
|
33
|
+
return cwd;
|
|
34
|
+
}
|
|
35
|
+
/** Match `git rev-parse --show-toplevel || pwd` without consulting HQ_ROOT. */
|
|
36
|
+
export function resolveWorkerCallerRoot(dependencies) {
|
|
37
|
+
const env = dependencies.env ?? process.env;
|
|
38
|
+
const fileSystem = dependencies.fs ?? SYSTEM_FS;
|
|
39
|
+
const git = runGit(["rev-parse", "--show-toplevel"], dependencies.cwd, env, dependencies.runGit);
|
|
40
|
+
if (git.code === 0) {
|
|
41
|
+
// Command substitution removes every trailing LF and no other whitespace.
|
|
42
|
+
return { root: git.stdout.replace(/\n+$/, ""), env, fs: fileSystem };
|
|
43
|
+
}
|
|
44
|
+
return { root: shellPwd(dependencies.cwd, env, fileSystem), env, fs: fileSystem };
|
|
45
|
+
}
|
|
46
|
+
export function errorCode(error) {
|
|
47
|
+
if (!error || typeof error !== "object" || !("code" in error))
|
|
48
|
+
return undefined;
|
|
49
|
+
const code = error.code;
|
|
50
|
+
return typeof code === "string" ? code : undefined;
|
|
51
|
+
}
|
|
52
|
+
export function missingOrUnreadable(error) {
|
|
53
|
+
return ["ENOENT", "ENOTDIR", "EACCES", "EPERM", "ELOOP"].includes(errorCode(error) ?? "");
|
|
54
|
+
}
|
|
55
|
+
export function isDirectory(filePath, fileSystem) {
|
|
56
|
+
try {
|
|
57
|
+
return fileSystem.statSync(filePath).isDirectory();
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
if (missingOrUnreadable(error))
|
|
61
|
+
return false;
|
|
62
|
+
throw error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function isSymlink(filePath, fileSystem) {
|
|
66
|
+
try {
|
|
67
|
+
return fileSystem.lstatSync(filePath).isSymbolicLink();
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (missingOrUnreadable(error))
|
|
71
|
+
return false;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function existsFollowingLinks(filePath, fileSystem) {
|
|
76
|
+
try {
|
|
77
|
+
fileSystem.statSync(filePath);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (missingOrUnreadable(error))
|
|
82
|
+
return false;
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=core-worker-skills-runtime.js.map
|
package/dist/commands/core.js
CHANGED
|
@@ -40,9 +40,11 @@ import { registerCoreJobsNativeCommand } from "./core-job-native.js";
|
|
|
40
40
|
import { registerCoreResolveCompanyNativeCommand } from "./core-resolve-company-command.js";
|
|
41
41
|
import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
|
|
42
42
|
import { registerCoreAuditLogNative } from "./core-audit-log-native.js";
|
|
43
|
+
import { registerCoreDmBindNative } from "./core-dm-bind-native.js";
|
|
43
44
|
import { registerCoreGitPackExtensionCheckNative } from "./core-git-pack-extension-check-native.js";
|
|
44
45
|
import { registerCorePinNative } from "./core-pin-native.js";
|
|
45
46
|
import { registerCoreRefreshVaultAccessNative } from "./core-refresh-vault-access-native.js";
|
|
47
|
+
import { registerCoreWorkMeshBindTrustedNative } from "./core-work-mesh-bind-trusted-native.js";
|
|
46
48
|
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
47
49
|
import { interpreterFor, runBundledScript } from "../utils/run-bundled-script.js";
|
|
48
50
|
import { CompanyIndexUserError, renderIndexTarget } from "../lib/index-render/index.js";
|
|
@@ -63,6 +65,7 @@ import { readSentryReportStdin, reportSentryEvent } from "../lib/core-utils/sent
|
|
|
63
65
|
import { CLEAN_HANDOFF_ASSET, RESUME_LOCK_ASSET, VERIFY_DELIVERABLES_ASSET, WORKTREE_GC_ASSET, runCleanWorktreeReconcileHandoff, runResumeThreadLock, runVerifyStoryDeliverables, runWorktreeGc, } from "../lib/core-utils/worktree-resume.js";
|
|
64
66
|
import { coreNativeHandoffEnabled } from "./core-native-handoff-flag.js";
|
|
65
67
|
import { coreCompanyIndexScopeEnabled } from "./core-company-index-scope-flag.js";
|
|
68
|
+
import { runWorkerSkillCommand } from "./core-worker-skills-command.js";
|
|
66
69
|
import { describeCoreCommand, registeredCoreCommandListing, } from "./core-command-catalog.js";
|
|
67
70
|
/**
|
|
68
71
|
* These commands are now native implementations. Their retained shell assets
|
|
@@ -227,6 +230,8 @@ export const SCAFFOLD_ONLY_ASSETS = [
|
|
|
227
230
|
{ asset: "core/scripts/hq-job-run.sh", root: "live", summary: "Scheduled jobs runner used by generated systemd units" },
|
|
228
231
|
{ asset: "core/scripts/hq-job-notify.sh", root: "live", summary: "Scheduled jobs failure notification helper" },
|
|
229
232
|
{ asset: "core/scripts/hq-job-remediate.sh", root: "live", summary: "Scheduled jobs bounded remediation helper" },
|
|
233
|
+
{ asset: "core/scripts/hq-dm-bind.sh", root: "live", summary: "Rollback shell for native dm-bind" },
|
|
234
|
+
{ asset: "core/scripts/work-mesh-live-bind-trusted.sh", root: "live", summary: "Rollback shell for native work-mesh-bind-trusted" },
|
|
230
235
|
// Shared shell libs sourced by the two relocated scripts (and hooks). Never
|
|
231
236
|
// dispatched on their own (so no `hq core` command), but they MUST ship in the
|
|
232
237
|
// tarball, or the bundled hq-session / checkpoint-stop-gate die at `source`
|
|
@@ -353,6 +358,7 @@ export function registerCoreCommands(program) {
|
|
|
353
358
|
const core = program
|
|
354
359
|
.command("core", { hidden: true })
|
|
355
360
|
.description("HQ scaffold scripts hosted by the CLI (core; not a stable surface)")
|
|
361
|
+
.addHelpText("afterAll", "exact-company-directory-match-v1")
|
|
356
362
|
// The root override lives on the GROUP, not the subcommands, so it can never
|
|
357
363
|
// collide with a wrapped script's own flags — everything after the
|
|
358
364
|
// subcommand name is passed through untouched.
|
|
@@ -372,6 +378,8 @@ export function registerCoreCommands(program) {
|
|
|
372
378
|
registerCoreGitPackExtensionCheckNative(core);
|
|
373
379
|
registerCorePinNative(core);
|
|
374
380
|
registerCoreRefreshVaultAccessNative(core);
|
|
381
|
+
registerCoreDmBindNative(core);
|
|
382
|
+
registerCoreWorkMeshBindTrustedNative(core);
|
|
375
383
|
// Wave 2 handoff/worktree commands retain their byte-identical shell assets
|
|
376
384
|
// as a default-off rollback path for one release. Keep the gate helper small
|
|
377
385
|
// so sibling Wave 2 work can reuse it without editing this command table.
|
|
@@ -472,10 +480,10 @@ export function registerCoreCommands(program) {
|
|
|
472
480
|
.allowExcessArguments()
|
|
473
481
|
.helpOption(false)
|
|
474
482
|
.argument("[args...]", "arguments passed through to the script")
|
|
475
|
-
.action((args = [], _opts, cmd) => {
|
|
483
|
+
.action(async (args = [], _opts, cmd) => {
|
|
476
484
|
const scope = core.opts();
|
|
477
485
|
const operands = cmd.args.length > 0 ? cmd.args : args;
|
|
478
|
-
|
|
486
|
+
await runWorkerSkillCommand(entry.name, operands, resolveEntryRoot(entry, scope));
|
|
479
487
|
});
|
|
480
488
|
describeCoreCommand(command, {
|
|
481
489
|
execution: interpreterFor(entry.asset) === "node" ? "bundled-node" : "bundled-shell",
|
package/dist/commands/dm.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export interface ChannelMember {
|
|
|
71
71
|
handle?: string;
|
|
72
72
|
}
|
|
73
73
|
/** A structured mention as the POST messages route expects it. */
|
|
74
|
-
|
|
74
|
+
interface ChannelMention {
|
|
75
75
|
participantUid: string;
|
|
76
76
|
participantType: "agent" | "human";
|
|
77
77
|
displayName: string;
|
|
@@ -133,7 +133,7 @@ export declare function resolveMentions(tokens: string[], members: ChannelMember
|
|
|
133
133
|
* Returns null on anything that doesn't match. Pure → unit-testable.
|
|
134
134
|
*/
|
|
135
135
|
export declare function parseDuration(input: string): number | null;
|
|
136
|
-
|
|
136
|
+
type DmAudience = "human" | "agent" | "both";
|
|
137
137
|
export interface DmSendBody {
|
|
138
138
|
toEmail?: string;
|
|
139
139
|
toPersonUid?: string;
|
|
@@ -321,7 +321,6 @@ export interface CallerIdentity {
|
|
|
321
321
|
* Pure → unit-testable.
|
|
322
322
|
*/
|
|
323
323
|
export declare function callerFromToken(token: string): CallerIdentity;
|
|
324
|
-
export declare function isCallerMessage(m: ChannelMessageItem, me: CallerIdentity): boolean;
|
|
325
324
|
/**
|
|
326
325
|
* Newest-first channel history → the caller's most recent top-level eventId.
|
|
327
326
|
* Replies (those with rootEventId) are skipped. Pure → unit-testable.
|
|
@@ -366,9 +365,6 @@ export declare function formatChannelTranscript(messages: ChannelMessageItem[],
|
|
|
366
365
|
*/
|
|
367
366
|
export declare function agentCallerUid(token: string, machine?: () => boolean): string | null;
|
|
368
367
|
export declare const AGENT_THREAD_UNAVAILABLE: string;
|
|
369
|
-
/** One-line summary of an agent inbox item (DM, email, … — shapes vary by channel). Pure. */
|
|
370
|
-
export declare function formatAgentInboxItem(item: Record<string, unknown>): string;
|
|
371
|
-
/** Remove delegated execution context before agent inbox items can be emitted. */
|
|
372
|
-
export declare function redactAgentInboxItem<T extends object>(item: T): Omit<T, "actingContext">;
|
|
373
368
|
export declare function registerDmCommand(program: Command): void;
|
|
369
|
+
export {};
|
|
374
370
|
//# sourceMappingURL=dm.d.ts.map
|
package/dist/commands/dm.js
CHANGED
|
@@ -892,7 +892,7 @@ export function callerFromToken(token) {
|
|
|
892
892
|
...(emailNorm ? { email: emailNorm } : {}),
|
|
893
893
|
};
|
|
894
894
|
}
|
|
895
|
-
|
|
895
|
+
function isCallerMessage(m, me) {
|
|
896
896
|
if (me.personUid && m.fromPersonUid === me.personUid)
|
|
897
897
|
return true;
|
|
898
898
|
if (me.email && (m.fromEmail ?? "").trim().toLowerCase() === me.email) {
|
|
@@ -1060,7 +1060,7 @@ export function agentCallerUid(token, machine = isMachineIdentity) {
|
|
|
1060
1060
|
export const AGENT_THREAD_UNAVAILABLE = "`hq dm thread` reads /v1/notify/thread, which is not available to agent identities. " +
|
|
1061
1061
|
"Use `hq dm inbox` — for agents it reads GET /v1/agents/{uid}/inbox.";
|
|
1062
1062
|
/** One-line summary of an agent inbox item (DM, email, … — shapes vary by channel). Pure. */
|
|
1063
|
-
|
|
1063
|
+
function formatAgentInboxItem(item) {
|
|
1064
1064
|
const str = (k) => (typeof item[k] === "string" ? item[k] : "");
|
|
1065
1065
|
const id = str("messageId") || str("id") || "?";
|
|
1066
1066
|
const channel = str("channel") || "inbox";
|
|
@@ -1070,7 +1070,7 @@ export function formatAgentInboxItem(item) {
|
|
|
1070
1070
|
return `${chalk.bold(from)} ${chalk.dim(`[${channel}] ${id}${when ? ` ${when}` : ""}`)}\n ${firstLine(text)}`;
|
|
1071
1071
|
}
|
|
1072
1072
|
/** Remove delegated execution context before agent inbox items can be emitted. */
|
|
1073
|
-
|
|
1073
|
+
function redactAgentInboxItem(item) {
|
|
1074
1074
|
const { actingContext: _actingContext, ...safe } = item;
|
|
1075
1075
|
return safe;
|
|
1076
1076
|
}
|
|
@@ -3,7 +3,7 @@ import { type McpManifest } from "./mcp-registration.js";
|
|
|
3
3
|
export type DesktopConnector = McpManifest & {
|
|
4
4
|
[key: string]: unknown;
|
|
5
5
|
};
|
|
6
|
-
export type ImportStatus = "imported" | "needs-signin" | "shared-for-local" | "skipped";
|
|
6
|
+
export type ImportStatus = "imported" | "needs-signin" | "shared-for-local" | "personal-for-local" | "skipped";
|
|
7
7
|
export interface ImportOutcome {
|
|
8
8
|
name: string;
|
|
9
9
|
status: ImportStatus;
|
|
@@ -15,6 +15,7 @@ import { bareProvider, IntegrationsCliError, printJson } from "./integrations-co
|
|
|
15
15
|
import { installIntegration, startOAuth } from "./integrations-api.js";
|
|
16
16
|
import { loadRevealedSecrets } from "./secrets.js";
|
|
17
17
|
import { registerServer } from "./mcp-registration.js";
|
|
18
|
+
import { personalConnectorSettingsEnabled } from "./personal-connector-settings-flag.js";
|
|
18
19
|
const OAUTH_REQUIRED_CODE = "INTEGRATION_FACTORY_OAUTH_REQUIRED";
|
|
19
20
|
/** Claude Desktop's documented connector-config location for the current OS. */
|
|
20
21
|
export function claudeDesktopConfigPath(platform = process.platform, home = os.homedir(), env = process.env) {
|
|
@@ -149,7 +150,13 @@ function oauthRequired(error) {
|
|
|
149
150
|
function reconnectCommand(name, provider) {
|
|
150
151
|
return `hq integrations reconnect ${name} --provider ${bareProvider(provider)}`;
|
|
151
152
|
}
|
|
152
|
-
function connectorPath(hqRoot, company,
|
|
153
|
+
function connectorPath(hqRoot, name, company, personal) {
|
|
154
|
+
if (personal) {
|
|
155
|
+
return path.join(hqRoot, "personal", "settings", "connectors", `${name}.json`);
|
|
156
|
+
}
|
|
157
|
+
if (!company) {
|
|
158
|
+
throw new IntegrationsCliError("A company is required for a shared connector manifest.", { expected: true });
|
|
159
|
+
}
|
|
153
160
|
return path.join(hqRoot, "companies", company, "settings", "connectors", `${name}.json`);
|
|
154
161
|
}
|
|
155
162
|
/**
|
|
@@ -172,8 +179,8 @@ async function companyFolderSlug(token, companyUid) {
|
|
|
172
179
|
}
|
|
173
180
|
return folderSlug;
|
|
174
181
|
}
|
|
175
|
-
function writeLocalConnector(hqRoot, company,
|
|
176
|
-
const target = connectorPath(hqRoot, company,
|
|
182
|
+
function writeLocalConnector(hqRoot, name, company, personal, entry) {
|
|
183
|
+
const target = connectorPath(hqRoot, name, company, personal);
|
|
177
184
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
178
185
|
fs.writeFileSync(target, `${JSON.stringify(stripConnectorSecrets(entry), null, 2)}\n`, { mode: 0o600 });
|
|
179
186
|
return target;
|
|
@@ -225,11 +232,13 @@ function renderOutcomes(outcomes) {
|
|
|
225
232
|
for (const outcome of outcomes) {
|
|
226
233
|
console.log(`${redactUrlsInText(outcome.name).padEnd(25)} ${outcome.status.padEnd(19)} ${redactUrlsInText(outcome.reason ?? outcome.path ?? "")}`);
|
|
227
234
|
}
|
|
228
|
-
for (const outcome of outcomes.filter((item) => item.status === "shared-for-local")) {
|
|
229
|
-
|
|
230
|
-
|
|
235
|
+
for (const outcome of outcomes.filter((item) => item.status === "shared-for-local" || item.status === "personal-for-local")) {
|
|
236
|
+
const label = outcome.status === "personal-for-local" ? "Personal manifest" : "Shared manifest";
|
|
237
|
+
const installLabel = outcome.status === "personal-for-local" ? "Personal install" : "Teammate install";
|
|
238
|
+
console.log(chalk.dim(` ${label}: ${redactUrlsInText(outcome.path ?? "")}`));
|
|
239
|
+
console.log(chalk.dim(` ${installLabel}: ${redactUrlsInText(outcome.installCommand ?? "")}`));
|
|
231
240
|
}
|
|
232
|
-
if (outcomes.some((item) => item.status === "shared-for-local")) {
|
|
241
|
+
if (outcomes.some((item) => item.status === "shared-for-local" || item.status === "personal-for-local")) {
|
|
233
242
|
console.log(chalk.yellow(" Local connector credentials were replaced with ${secret:ENV_NAME}. Set them with `hq secrets` before installing."));
|
|
234
243
|
}
|
|
235
244
|
if (outcomes.some((item) => item.redactedArgumentCredentials)) {
|
|
@@ -245,7 +254,7 @@ function renderOutcomes(outcomes) {
|
|
|
245
254
|
export function registerImportCommands(integrations) {
|
|
246
255
|
integrations
|
|
247
256
|
.command("import")
|
|
248
|
-
.description("Import Claude Desktop connectors into
|
|
257
|
+
.description("Import Claude Desktop connectors into HQ Integrations")
|
|
249
258
|
.option("--company <slug>", "Company slug (defaults to your single active company)")
|
|
250
259
|
.option("--dry-run", "Detect and classify connectors without writing or connecting")
|
|
251
260
|
.option("--only <name[,name...]>", "Import only these Claude Desktop connector names")
|
|
@@ -272,6 +281,7 @@ export function registerImportCommands(integrations) {
|
|
|
272
281
|
const outcomes = [];
|
|
273
282
|
let company;
|
|
274
283
|
let hqRoot;
|
|
284
|
+
const personalSettings = personalConnectorSettingsEnabled();
|
|
275
285
|
const localCompany = async () => company ??= await companyFolderSlug(token, companyUid);
|
|
276
286
|
const localHqRoot = () => hqRoot ??= resolveDefaultHqRoot({ onMissing: "throw" });
|
|
277
287
|
const normalizedNames = new Map();
|
|
@@ -297,13 +307,17 @@ export function registerImportCommands(integrations) {
|
|
|
297
307
|
continue;
|
|
298
308
|
}
|
|
299
309
|
if (isLocal(entry)) {
|
|
300
|
-
const resolvedCompany = await localCompany();
|
|
301
|
-
const target = connectorPath(localHqRoot(), resolvedCompany,
|
|
310
|
+
const resolvedCompany = personalSettings ? undefined : await localCompany();
|
|
311
|
+
const target = connectorPath(localHqRoot(), safeName, resolvedCompany, personalSettings);
|
|
302
312
|
outcomes.push({
|
|
303
313
|
name,
|
|
304
|
-
status: "shared-for-local",
|
|
305
|
-
...(opts.dryRun
|
|
306
|
-
|
|
314
|
+
status: personalSettings ? "personal-for-local" : "shared-for-local",
|
|
315
|
+
...(opts.dryRun
|
|
316
|
+
? { reason: personalSettings ? "would write secret-stripped personal manifest" : "would write secret-stripped local manifest" }
|
|
317
|
+
: { path: writeLocalConnector(localHqRoot(), safeName, resolvedCompany, personalSettings, entry) }),
|
|
318
|
+
installCommand: personalSettings
|
|
319
|
+
? `hq integrations install-local ${safeName}`
|
|
320
|
+
: `hq integrations install-local ${safeName} --company ${resolvedCompany}`,
|
|
307
321
|
...(hasRedactedArgumentCredentials(entry) ? { redactedArgumentCredentials: true } : {}),
|
|
308
322
|
});
|
|
309
323
|
// Keep dry-run output useful without leaking a filesystem write target.
|
|
@@ -324,7 +338,7 @@ export function registerImportCommands(integrations) {
|
|
|
324
338
|
});
|
|
325
339
|
integrations
|
|
326
340
|
.command("install-local <name>")
|
|
327
|
-
.description("
|
|
341
|
+
.description("Install local connector settings in this machine's Claude/Codex MCP config")
|
|
328
342
|
.option("--company <slug>", "Company slug (defaults to your single active company)")
|
|
329
343
|
.option("--json", "Machine-readable registration result")
|
|
330
344
|
.action(async (name, opts) => {
|
|
@@ -334,11 +348,12 @@ export function registerImportCommands(integrations) {
|
|
|
334
348
|
}
|
|
335
349
|
const token = await ensureCognitoToken();
|
|
336
350
|
const companyUid = await getCompanyUid(token, opts.company);
|
|
337
|
-
const
|
|
351
|
+
const personalSettings = personalConnectorSettingsEnabled();
|
|
352
|
+
const company = personalSettings ? undefined : await companyFolderSlug(token, companyUid);
|
|
338
353
|
const hqRoot = resolveDefaultHqRoot({ onMissing: "throw" });
|
|
339
|
-
const manifestPath = connectorPath(hqRoot, company,
|
|
354
|
+
const manifestPath = connectorPath(hqRoot, safeName, company, personalSettings);
|
|
340
355
|
if (!fs.existsSync(manifestPath)) {
|
|
341
|
-
throw new IntegrationsCliError(`
|
|
356
|
+
throw new IntegrationsCliError(`Connector settings not found: ${manifestPath}`, { expected: true });
|
|
342
357
|
}
|
|
343
358
|
let manifest;
|
|
344
359
|
try {
|
|
@@ -346,7 +361,7 @@ export function registerImportCommands(integrations) {
|
|
|
346
361
|
}
|
|
347
362
|
catch (error) {
|
|
348
363
|
const message = error instanceof Error ? error.message : String(error);
|
|
349
|
-
throw new IntegrationsCliError(`Could not parse
|
|
364
|
+
throw new IntegrationsCliError(`Could not parse connector settings ${manifestPath}: ${message}`, { expected: true });
|
|
350
365
|
}
|
|
351
366
|
const secretNames = [...Object.values({ ...(manifest.env ?? {}), ...(manifest.headers ?? {}) }), ...(manifest.args ?? [])]
|
|
352
367
|
.flatMap((value) => [...value.matchAll(/\$\{secret:([A-Z][A-Z0-9_]*(?:\/[A-Z][A-Z0-9_]*)*)\}/g)].map((match) => match[1]));
|
|
@@ -356,7 +371,7 @@ export function registerImportCommands(integrations) {
|
|
|
356
371
|
const result = registerServer({
|
|
357
372
|
name: safeName,
|
|
358
373
|
manifest,
|
|
359
|
-
pack: `company-${company}-connectors`,
|
|
374
|
+
pack: personalSettings ? "personal-connectors" : `company-${company}-connectors`,
|
|
360
375
|
resolveSecret: (secret) => secrets.get(secret) ?? null,
|
|
361
376
|
});
|
|
362
377
|
if (opts.json)
|
|
@@ -165,12 +165,13 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
165
165
|
}
|
|
166
166
|
const hqRoot = ctx.hqRoot();
|
|
167
167
|
const config = loadLanesConfig(hqRoot);
|
|
168
|
+
const quotaState = loadQuotaState(hqRoot);
|
|
168
169
|
const loaded = await loadClaudeBalanceHostSnapshots(ctx, hqRoot, config);
|
|
169
170
|
// Retain the established response field for clients; balancing is now
|
|
170
171
|
// unconditional, so this compatibility value is always true.
|
|
171
172
|
const balanceGateEnabled = true;
|
|
172
173
|
const recentUsageEnabled = await deps.readClaudeAccountBalanceSignalsEnabled(ctx);
|
|
173
|
-
const loads = accountLoadsForReport({ hosts: loaded.snapshots, config });
|
|
174
|
+
const loads = accountLoadsForReport({ hosts: loaded.snapshots, config, quotaState });
|
|
174
175
|
const suggestions = planClaudeBalanceMoves({ hosts: loaded.snapshots, config });
|
|
175
176
|
const selections = loaded.snapshots.map((host) => {
|
|
176
177
|
try {
|
|
@@ -178,6 +179,7 @@ export function createLaneAccountsHandlers(deps) {
|
|
|
178
179
|
hosts: loaded.snapshots,
|
|
179
180
|
targetHost: host.capacity.host,
|
|
180
181
|
config,
|
|
182
|
+
quotaState,
|
|
181
183
|
recentUsageEnabled,
|
|
182
184
|
now: ctx.now(),
|
|
183
185
|
unavailableHosts: loaded.unavailableHosts,
|
|
@@ -396,9 +396,31 @@ export function createLaneMessageHandlers(deps) {
|
|
|
396
396
|
if (opts.model !== undefined && opts.account === undefined) {
|
|
397
397
|
throw new LanesError("model_requires_account", "--model on hq lanes message is only accepted together with --account", {});
|
|
398
398
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
399
|
+
let quotaHoldDeferred;
|
|
400
|
+
let automaticAccountSelection = undefined;
|
|
401
|
+
if (opts.account === undefined && mechanism === "resume") {
|
|
402
|
+
try {
|
|
403
|
+
automaticAccountSelection = await selectAutomaticClaudeResumeAccount(ctx, lane, resumeProfile);
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
const reasons = err instanceof LanesError && err.error === "claude_account_balance_no_eligible_account" &&
|
|
407
|
+
Array.isArray(err.details.account_reasons)
|
|
408
|
+
? err.details.account_reasons.filter((reason) => reason !== null && typeof reason === "object" && !Array.isArray(reason))
|
|
409
|
+
: [];
|
|
410
|
+
const held = reasons.filter((reason) => reason.reason === "quota_held");
|
|
411
|
+
if (held.length === 0)
|
|
412
|
+
throw err;
|
|
413
|
+
const resetTimes = held
|
|
414
|
+
.map((reason) => reason.quota_reset_at)
|
|
415
|
+
.filter((value) => typeof value === "string" && Number.isFinite(Date.parse(value)))
|
|
416
|
+
.sort((a, b) => Date.parse(a) - Date.parse(b));
|
|
417
|
+
quotaHoldDeferred = {
|
|
418
|
+
...(resetTimes[0] !== undefined ? { quota_reset_at: resetTimes[0] } : {}),
|
|
419
|
+
...(lane.model !== undefined ? { model: lane.model } : {}),
|
|
420
|
+
};
|
|
421
|
+
automaticAccountSelection = undefined;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
402
424
|
const resumeEffort = resolveResumeEffort(ctx, lane, {
|
|
403
425
|
cliEffort,
|
|
404
426
|
profile: resumeProfile,
|
|
@@ -488,7 +510,49 @@ export function createLaneMessageHandlers(deps) {
|
|
|
488
510
|
let resumed = null;
|
|
489
511
|
let move = null;
|
|
490
512
|
let queuedOnAdmission = false;
|
|
491
|
-
|
|
513
|
+
let queuedOnQuotaHold = false;
|
|
514
|
+
if (quotaHoldDeferred !== undefined) {
|
|
515
|
+
const sessionId = lane.provider_session_id;
|
|
516
|
+
if (!sessionId) {
|
|
517
|
+
throw new LanesError("no_provider_session", `Lane ${laneId} has no provider session id to queue this message`, {
|
|
518
|
+
lane_id: laneId,
|
|
519
|
+
provider: lane.provider,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
lane = casUpdateLane(hqRoot, laneId, (current) => {
|
|
523
|
+
if (current.version !== lane.version || current.state !== lane.state) {
|
|
524
|
+
throw new LanesError("lane_admission_conflict", `Lane ${laneId} changed while its quota-held message was queued`, {
|
|
525
|
+
lane_id: laneId,
|
|
526
|
+
prepared_version: lane.version,
|
|
527
|
+
version: current.version,
|
|
528
|
+
state: current.state,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return {
|
|
532
|
+
...current,
|
|
533
|
+
state: "queued",
|
|
534
|
+
queued_reason: "account_retry",
|
|
535
|
+
queued_details: undefined,
|
|
536
|
+
queued_at: at,
|
|
537
|
+
admission_reserved_at: undefined,
|
|
538
|
+
pending_resume: {
|
|
539
|
+
session_id: sessionId,
|
|
540
|
+
trigger: "message",
|
|
541
|
+
caller: ctx.env.HQ_LANE_ID?.trim() || "user",
|
|
542
|
+
text: deliveryText,
|
|
543
|
+
...(opts.monitorEvent === undefined ? {} : { monitor_event: true }),
|
|
544
|
+
message_id: messageId,
|
|
545
|
+
compact: opts.compact !== false,
|
|
546
|
+
at,
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
});
|
|
550
|
+
queuedOnQuotaHold = true;
|
|
551
|
+
storySwitchDeliveryStarted = true;
|
|
552
|
+
markAccepted();
|
|
553
|
+
deps.onLaneMessageQueued?.(laneId, "quota_hold");
|
|
554
|
+
}
|
|
555
|
+
else if (opts.account !== undefined && !keepCurrentAccount) {
|
|
492
556
|
// A moved message is always a resume on the target account: a running
|
|
493
557
|
// lane (the dropbox mechanism) is refused inside prepareAccountMove.
|
|
494
558
|
move = await prepareAccountMove(ctx, lane, {
|
|
@@ -556,7 +620,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
556
620
|
});
|
|
557
621
|
}
|
|
558
622
|
let questionCancellation = null;
|
|
559
|
-
if (move?.queued) {
|
|
623
|
+
if (move?.queued || queuedOnQuotaHold) {
|
|
560
624
|
// Parked at the target's cap: reconcile resumes the session there.
|
|
561
625
|
markAccepted();
|
|
562
626
|
questionCancellation = awaitingInputFallback
|
|
@@ -678,11 +742,13 @@ export function createLaneMessageHandlers(deps) {
|
|
|
678
742
|
const drainHook = drainHookStatusFor(ctx, next);
|
|
679
743
|
const deliveryStatus = move?.queued
|
|
680
744
|
? "queued_on_target_account"
|
|
681
|
-
:
|
|
682
|
-
? "
|
|
683
|
-
:
|
|
684
|
-
? "
|
|
685
|
-
: "
|
|
745
|
+
: queuedOnQuotaHold
|
|
746
|
+
? "queued_on_quota_hold"
|
|
747
|
+
: queuedOnAdmission
|
|
748
|
+
? "queued_on_admission"
|
|
749
|
+
: mechanism === "dropbox"
|
|
750
|
+
? "awaiting_next_tool_call"
|
|
751
|
+
: "worker_spawned";
|
|
686
752
|
const warning = [
|
|
687
753
|
eligibility.supersededMessageWarning,
|
|
688
754
|
mechanism === "dropbox" && drainHook !== null && !drainHook.registered
|
|
@@ -702,6 +768,14 @@ export function createLaneMessageHandlers(deps) {
|
|
|
702
768
|
delivery: mechanism,
|
|
703
769
|
delivered_to: { host: lane.host.id, lane_id: laneId, mechanism },
|
|
704
770
|
delivery_status: deliveryStatus,
|
|
771
|
+
...(queuedOnQuotaHold ? {
|
|
772
|
+
queued_reason: "account_quota_held",
|
|
773
|
+
quota_hold: {
|
|
774
|
+
reason: "account_quota_held",
|
|
775
|
+
quota_reset_at: quotaHoldDeferred?.quota_reset_at ?? null,
|
|
776
|
+
model: quotaHoldDeferred?.model ?? null,
|
|
777
|
+
},
|
|
778
|
+
} : {}),
|
|
705
779
|
inbox_path: mechanism === "dropbox" ? dropboxInboxPath(hqRoot, laneId) : null,
|
|
706
780
|
drain_hook: drainHook,
|
|
707
781
|
warning,
|
|
@@ -156,7 +156,7 @@ export interface LaneMessageHandlerDeps {
|
|
|
156
156
|
/** Test seam invoked after a provider worker has been started. */
|
|
157
157
|
onLaneWorkerStarted?: (laneId: string, command: "message" | "resume") => void;
|
|
158
158
|
/** Test seam invoked after a message has been durably queued for a lane. */
|
|
159
|
-
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move") => void;
|
|
159
|
+
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move" | "quota_hold") => void;
|
|
160
160
|
archiveResumeMessage(hqRoot: string, laneId: string, message: {
|
|
161
161
|
message_id: string;
|
|
162
162
|
[key: string]: unknown;
|
|
@@ -264,6 +264,9 @@ export interface Ctx {
|
|
|
264
264
|
backfillOutpostIdentity: (expectedId: string) => "created" | "unchanged";
|
|
265
265
|
fetchText: (url: string) => Promise<string>;
|
|
266
266
|
lanePlacementEnabled: (company: LaneCompanyRef | undefined) => Promise<boolean>;
|
|
267
|
+
readLeadAutopilotFlag: (flag: string, options: {
|
|
268
|
+
company: LaneCompanyRef;
|
|
269
|
+
}) => Promise<boolean>;
|
|
267
270
|
storyLedgerEnabled: () => boolean;
|
|
268
271
|
isSsmOwnerDispatch: () => boolean;
|
|
269
272
|
withGrantHireLocks: typeof withLaneHireLocks;
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface LanesCommandDeps {
|
|
|
32
32
|
/** Test seam for failures after a lane provider worker is started. */
|
|
33
33
|
onLaneWorkerStarted?: (laneId: string, command: "message" | "resume") => void;
|
|
34
34
|
/** Test seam for failures after a message is durably queued. */
|
|
35
|
-
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move") => void;
|
|
35
|
+
onLaneMessageQueued?: (laneId: string, path: "admission" | "account_move" | "quota_hold") => void;
|
|
36
36
|
hqRoot?: () => string;
|
|
37
37
|
/** Start HQ-root walk here instead of process.cwd(). Tests only. */
|
|
38
38
|
cwd?: () => string;
|
|
@@ -40,6 +40,9 @@ export interface LanesCommandDeps {
|
|
|
40
40
|
now?: () => Date;
|
|
41
41
|
hostname?: () => string;
|
|
42
42
|
resolveCompany?: (company?: string) => Promise<LaneCompanyRef>;
|
|
43
|
+
readLeadAutopilotFlag?: (flag: string, options: {
|
|
44
|
+
company: LaneCompanyRef;
|
|
45
|
+
}) => Promise<boolean>;
|
|
43
46
|
/** Membership-backed explicit company selection for account-home writers. */
|
|
44
47
|
resolveActiveCompany?: (company: string) => Promise<LaneCompanyRef | null>;
|
|
45
48
|
listOutpostIds?: () => Promise<string[]>;
|