@indigoai-us/hq-cli 5.345.187 → 5.345.189
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 +111 -0
- package/dist/command-catalog.generated.d.ts +34 -0
- package/dist/command-catalog.generated.js +42 -0
- package/dist/commands/agent-inbox.js +43 -11
- package/dist/commands/agent-mcp.js +5 -2
- package/dist/commands/bot.js +15 -3
- package/dist/commands/core-company-index-scope-flag.d.ts +2 -1
- package/dist/commands/core-company-index-scope-flag.js +2 -1
- package/dist/commands/core.js +0 -5
- package/dist/commands/dm-attach.d.ts +73 -0
- package/dist/commands/dm-attach.js +104 -0
- package/dist/commands/dm.d.ts +10 -0
- package/dist/commands/dm.js +114 -2
- package/dist/commands/sources.js +15 -0
- package/dist/hq-cloud-sync-runner/bin/sync-runner.js +57330 -63803
- package/dist/hq-cloud-sync-runner/package.json +1 -1
- package/dist/lib/agent-kit/inbox-fetch.d.ts +92 -0
- package/dist/lib/agent-kit/inbox-fetch.js +135 -0
- package/dist/lib/agent-kit/mcp/tools.d.ts +11 -2
- package/dist/lib/agent-kit/mcp/tools.js +17 -3
- package/dist/lib/bot/fleet-local.d.ts +3 -1
- package/dist/lib/bot/fleet-local.js +9 -3
- package/dist/lib/bot/fleet-python.d.ts +6 -0
- package/dist/lib/bot/fleet-python.js +24 -5
- package/dist/lib/bot/probe.d.ts +7 -1
- package/dist/lib/bot/probe.js +11 -3
- package/dist/lib/bot/run.d.ts +9 -0
- package/dist/lib/bot/run.js +114 -8
- package/dist/lib/bot/runtime-failure.d.ts +3 -0
- package/dist/lib/bot/runtime-failure.js +20 -2
- 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/index-render/session-scope.d.ts +3 -3
- package/dist/lib/index-render/session-scope.js +11 -9
- package/dist/lib/index-render/shared.d.ts +6 -4
- package/dist/lib/index-render/shared.js +33 -4
- package/dist/utils/cognito-session.d.ts +5 -3
- package/dist/utils/cognito-session.js +43 -46
- package/dist/utils/hq-root-resolver.d.ts +27 -1
- package/dist/utils/hq-root-resolver.js +65 -33
- package/package.json +15 -15
package/dist/commands/dm.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join as pathJoin } from "node:path";
|
|
3
4
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
4
5
|
import { resolveCallerPersonUid, resolveDestinationCompanyUid, vaultApiFetch } from "../utils/vault-api.js";
|
|
5
6
|
import { isMachineIdentity } from "@indigoai-us/hq-cloud";
|
|
@@ -10,7 +11,7 @@ import { EMAIL_PATTERN, RECIPIENT_UID_PATTERN, PERSON_AGENT_AUDIENCE_MESSAGE, PE
|
|
|
10
11
|
import { callerIsMachinePrincipal } from "../lib/caller-principal.js";
|
|
11
12
|
export { defaultAudienceFor, recipientKindOf };
|
|
12
13
|
import { dmForwardCliEnabled } from "./dm-forward-flag.js";
|
|
13
|
-
import { attachmentScopeForChannel, formatUploadedAttachments, personalScopeForCaller, uploadDmAttachments, validateAttachmentPaths, } from "./dm-attach.js";
|
|
14
|
+
import { attachmentReadScopes, attachmentScopeForChannel, downloadChatAttachment, findMessageAttachments, parseChatAttachmentKey, sanitizeAttachmentName, formatUploadedAttachments, personalScopeForCaller, uploadDmAttachments, validateAttachmentPaths, } from "./dm-attach.js";
|
|
14
15
|
import { ChannelNotFoundError, suggestChannels, withChannelSuggestions, } from "./channel-suggest.js";
|
|
15
16
|
/**
|
|
16
17
|
* Classify a recipient arg as an email or a person/agent uid. Mirrors the
|
|
@@ -1679,6 +1680,107 @@ async function runDmInbox(opts) {
|
|
|
1679
1680
|
process.exit(1);
|
|
1680
1681
|
}
|
|
1681
1682
|
}
|
|
1683
|
+
/**
|
|
1684
|
+
* Where to write each downloaded attachment. One file and an `--out` that is
|
|
1685
|
+
* not an existing directory: write exactly there. Otherwise `--out` (default
|
|
1686
|
+
* cwd) is a directory and each file keeps its sanitized name. Pure apart from
|
|
1687
|
+
* the injected `isDir`.
|
|
1688
|
+
*/
|
|
1689
|
+
export function attachmentOutputPaths(refs, out, isDir) {
|
|
1690
|
+
const nameOf = (r) => sanitizeAttachmentName(r.name?.trim() || r.vaultPath.split("/").pop() || "file");
|
|
1691
|
+
if (refs.length === 1 && out && !isDir(out))
|
|
1692
|
+
return [out];
|
|
1693
|
+
const dir = out ?? ".";
|
|
1694
|
+
// Two files with the same sanitized name get distinct targets: name-1.ext, name-2.ext.
|
|
1695
|
+
const used = new Set();
|
|
1696
|
+
return refs.map((r) => {
|
|
1697
|
+
const base = nameOf(r);
|
|
1698
|
+
let name = base;
|
|
1699
|
+
for (let n = 1; used.has(name); n++) {
|
|
1700
|
+
const dot = base.lastIndexOf(".");
|
|
1701
|
+
name = dot > 0 ? `${base.slice(0, dot)}-${n}${base.slice(dot)}` : `${base}-${n}`;
|
|
1702
|
+
}
|
|
1703
|
+
used.add(name);
|
|
1704
|
+
return pathJoin(dir, name);
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
async function runDmAttachment(ref, opts) {
|
|
1708
|
+
try {
|
|
1709
|
+
const token = await ensureCognitoToken();
|
|
1710
|
+
const callerUid = agentCallerUid(token) ?? (await callerUidForAttach(token));
|
|
1711
|
+
let refs;
|
|
1712
|
+
if (parseChatAttachmentKey(ref)) {
|
|
1713
|
+
refs = [{ vaultPath: ref.trim() }];
|
|
1714
|
+
}
|
|
1715
|
+
else {
|
|
1716
|
+
if (agentCallerUid(token)) {
|
|
1717
|
+
throw new Error("Agents cannot look up an eventId in the person inbox. Pass the attachment's vault path instead.");
|
|
1718
|
+
}
|
|
1719
|
+
const res = await vaultApiFetch({
|
|
1720
|
+
token,
|
|
1721
|
+
path: "/v1/notify/inbox",
|
|
1722
|
+
query: { limit: opts.limit ?? "100" },
|
|
1723
|
+
});
|
|
1724
|
+
if (!res.ok) {
|
|
1725
|
+
const err = (await res.json().catch(() => ({})));
|
|
1726
|
+
throw new Error(friendlyDmError(res.status, err.code, err.error ?? err.message ?? res.statusText));
|
|
1727
|
+
}
|
|
1728
|
+
const data = (await res.json());
|
|
1729
|
+
const found = findMessageAttachments(data.events ?? [], ref);
|
|
1730
|
+
if (found === null) {
|
|
1731
|
+
throw new Error(`No message ${ref} in your last ${opts.limit ?? "100"} inbox messages. Raise --limit, or pass the attachment's vault path.`);
|
|
1732
|
+
}
|
|
1733
|
+
if (found.length === 0)
|
|
1734
|
+
throw new Error(`Message ${ref} has no attachments.`);
|
|
1735
|
+
refs = found;
|
|
1736
|
+
}
|
|
1737
|
+
const targets = attachmentOutputPaths(refs, opts.out, (p) => {
|
|
1738
|
+
try {
|
|
1739
|
+
return statSync(p).isDirectory();
|
|
1740
|
+
}
|
|
1741
|
+
catch {
|
|
1742
|
+
return false;
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
// Check every destination before writing anything, so a clash never leaves
|
|
1746
|
+
// a partial download behind.
|
|
1747
|
+
if (!opts.force) {
|
|
1748
|
+
const clash = targets.find((dest) => existsSync(dest));
|
|
1749
|
+
if (clash) {
|
|
1750
|
+
throw new Error(`${clash} already exists. Pass --force to overwrite, or --out to choose another path.`);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
const scopes = refs.map((r) => attachmentReadScopes({
|
|
1754
|
+
vaultPath: r.vaultPath,
|
|
1755
|
+
companyUid: opts.company ?? r.companyUid,
|
|
1756
|
+
callerUid,
|
|
1757
|
+
senderUid: r.senderUid,
|
|
1758
|
+
}));
|
|
1759
|
+
for (let i = 0; i < refs.length; i++) {
|
|
1760
|
+
const r = refs[i];
|
|
1761
|
+
const dest = targets[i];
|
|
1762
|
+
let bytes;
|
|
1763
|
+
let lastErr;
|
|
1764
|
+
for (const scopeUid of scopes[i]) {
|
|
1765
|
+
try {
|
|
1766
|
+
bytes = await downloadChatAttachment({ token, scopeUid, vaultPath: r.vaultPath });
|
|
1767
|
+
break;
|
|
1768
|
+
}
|
|
1769
|
+
catch (e) {
|
|
1770
|
+
lastErr = e;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
if (!bytes)
|
|
1774
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
1775
|
+
writeFileSync(dest, bytes);
|
|
1776
|
+
console.log(chalk.green(`Saved ${dest}`) + chalk.dim(` (${bytes.length} bytes)`));
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
catch (err) {
|
|
1780
|
+
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
|
1781
|
+
process.exit(1);
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1682
1784
|
async function runDmThread(identifier, opts) {
|
|
1683
1785
|
try {
|
|
1684
1786
|
const query = buildThreadQuery(identifier);
|
|
@@ -1879,6 +1981,16 @@ export function registerDmCommand(program) {
|
|
|
1879
1981
|
.action(async (opts) => {
|
|
1880
1982
|
await runDmInbox(opts);
|
|
1881
1983
|
});
|
|
1984
|
+
dm
|
|
1985
|
+
.command("attachment <ref>")
|
|
1986
|
+
.description("Download a file attached to a DM. REF is the message eventId from `hq dm inbox` (downloads every file on it) or the attachment's vault path (chat/attachments/...). The file is read from the sender's vault; only the two people in the conversation can open a DM attachment.")
|
|
1987
|
+
.option("--out <path>", "Where to write: a file path for one attachment, else a directory (default: current directory)")
|
|
1988
|
+
.option("--company <uid>", "Vault scope the file lives in (the companyUid shown on the message). Derived from the DM pair when omitted")
|
|
1989
|
+
.option("--force", "Overwrite an existing file")
|
|
1990
|
+
.option("--limit <n>", "How many recent inbox messages to search for the eventId", "100")
|
|
1991
|
+
.action(async (ref, opts) => {
|
|
1992
|
+
await runDmAttachment(ref, opts);
|
|
1993
|
+
});
|
|
1882
1994
|
dm
|
|
1883
1995
|
.command("thread <person>")
|
|
1884
1996
|
.alias("read")
|
package/dist/commands/sources.js
CHANGED
|
@@ -43,6 +43,7 @@ import { createPersonVaultReader } from "../lib/hot-channels/person-vault-reader
|
|
|
43
43
|
import { S3Client } from "@aws-sdk/client-s3";
|
|
44
44
|
import { resolveCanonicalPersonUid } from "./cloud.js";
|
|
45
45
|
import { botsRoot as defaultBotsRoot } from "../lib/bot/paths.js";
|
|
46
|
+
import { ensureHotSignalSyncJob } from "../lib/bot/jobs/hot-signal-sync.js";
|
|
46
47
|
import { findHotSignalSyncJobs, lastRunsPerAdapter, parseDailyAt, renderScheduleReport, rescheduleDaily, toJobView, } from "../lib/hot-channels/schedule.js";
|
|
47
48
|
import { listActiveMembers } from "./members.js";
|
|
48
49
|
import { runOnDemandSync, } from "../lib/hot-channels/run.js";
|
|
@@ -619,6 +620,20 @@ export async function runSchedule(options, overrides = {}) {
|
|
|
619
620
|
parseDailyAt(options.at);
|
|
620
621
|
let found = findHotSignalSyncJobs(overrides.botsRoot ?? defaultBotsRoot(), company);
|
|
621
622
|
if (options.at !== undefined) {
|
|
623
|
+
if (found.length === 0) {
|
|
624
|
+
// Setting a time turns the daily sync on: register it on the personal setup bot.
|
|
625
|
+
const root = overrides.botsRoot ?? defaultBotsRoot();
|
|
626
|
+
const setupDir = path.join(root, "setup");
|
|
627
|
+
if (fs.existsSync(setupDir)) {
|
|
628
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
629
|
+
const otherBotDirs = fs
|
|
630
|
+
.readdirSync(root, { withFileTypes: true })
|
|
631
|
+
.filter((d) => d.isDirectory())
|
|
632
|
+
.map((d) => path.join(root, d.name));
|
|
633
|
+
ensureHotSignalSyncJob(setupDir, { company, timezone, otherBotDirs }, now);
|
|
634
|
+
found = findHotSignalSyncJobs(root, company);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
622
637
|
if (found.length === 0)
|
|
623
638
|
throw new NoHotSignalSyncJobError(company);
|
|
624
639
|
found = rescheduleDaily(found, options.at, now);
|