@indigoai-us/hq-cli 5.119.6 → 5.119.8
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 +19 -0
- package/dist/commands/dm.d.ts +7 -0
- package/dist/commands/dm.js +13 -1
- package/dist/lib/agent-kit/fallback.d.ts +0 -14
- package/dist/lib/agent-kit/fallback.js +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.119.8] — 2026-09-17
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Channel posts that mention people by full name no longer show the parsing
|
|
10
|
+
quotes in the room. `@"Jacob Posel"` is still how a multi-word name is
|
|
11
|
+
typed, but the message is posted as `@Jacob Posel`, the way a person would
|
|
12
|
+
have written it. The structured mention itself is unchanged.
|
|
13
|
+
|
|
14
|
+
## [5.119.7] — 2026-09-17
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- When the kit cannot install its `@reboot` entry because the host has no
|
|
19
|
+
`crontab` at all, the install now says so and names the package to install,
|
|
20
|
+
instead of reporting a raw ENOENT the operator cannot act on. A minimal
|
|
21
|
+
container image routinely ships without cron; the Muse pilot hit this the
|
|
22
|
+
first time the reboot entry was attempted.
|
|
23
|
+
|
|
5
24
|
## [5.119.6] — 2026-09-17
|
|
6
25
|
|
|
7
26
|
### Fixed
|
package/dist/commands/dm.d.ts
CHANGED
|
@@ -97,6 +97,13 @@ export declare function mentionParticipantType(uid: string): "agent" | "human";
|
|
|
97
97
|
* Trailing punctuation is trimmed ("@Izzy," → "Izzy"), so a name is never
|
|
98
98
|
* polluted by the sentence around it.
|
|
99
99
|
*/
|
|
100
|
+
/**
|
|
101
|
+
* Once a quoted token has resolved to a real mention, the quotes have done
|
|
102
|
+
* their job (they only exist so a multi-word name parses as one token). Post
|
|
103
|
+
* the body with `@"Jacob Posel"` rewritten to `@Jacob Posel`, which is how a
|
|
104
|
+
* person would have typed it and how the room should read it. Pure.
|
|
105
|
+
*/
|
|
106
|
+
export declare function stripMentionQuotes(body: string): string;
|
|
100
107
|
export declare function parseMentionTokens(body: string): string[];
|
|
101
108
|
/**
|
|
102
109
|
* The names one roster row answers to, lowercased: its display name, the slug of
|
package/dist/commands/dm.js
CHANGED
|
@@ -138,6 +138,15 @@ export function mentionParticipantType(uid) {
|
|
|
138
138
|
* Trailing punctuation is trimmed ("@Izzy," → "Izzy"), so a name is never
|
|
139
139
|
* polluted by the sentence around it.
|
|
140
140
|
*/
|
|
141
|
+
/**
|
|
142
|
+
* Once a quoted token has resolved to a real mention, the quotes have done
|
|
143
|
+
* their job (they only exist so a multi-word name parses as one token). Post
|
|
144
|
+
* the body with `@"Jacob Posel"` rewritten to `@Jacob Posel`, which is how a
|
|
145
|
+
* person would have typed it and how the room should read it. Pure.
|
|
146
|
+
*/
|
|
147
|
+
export function stripMentionQuotes(body) {
|
|
148
|
+
return body.replace(/(^|[\s([{<])@"([^"\n]{1,80})"/g, "$1@$2");
|
|
149
|
+
}
|
|
141
150
|
export function parseMentionTokens(body) {
|
|
142
151
|
const pattern = /(^|[\s([{<])@(?:"([^"\n]{1,80})"|([A-Za-z0-9][A-Za-z0-9._-]*))/g;
|
|
143
152
|
const names = [];
|
|
@@ -515,7 +524,7 @@ async function fetchChannelMembers(token, channelId) {
|
|
|
515
524
|
*/
|
|
516
525
|
async function runChannelSend(channelName, message, opts) {
|
|
517
526
|
try {
|
|
518
|
-
|
|
527
|
+
let body = (message ?? "").trim();
|
|
519
528
|
if (!body) {
|
|
520
529
|
console.error(chalk.red(`A message body is required: hq dm ${channelName} "<message>" (or hq dm --channel ${channelName} "<message>").`));
|
|
521
530
|
process.exit(1);
|
|
@@ -560,6 +569,9 @@ async function runChannelSend(channelName, message, opts) {
|
|
|
560
569
|
mentions = resolved.mentions;
|
|
561
570
|
}
|
|
562
571
|
}
|
|
572
|
+
// Send the human-readable form: the quotes only existed for parsing.
|
|
573
|
+
if (mentions.length > 0)
|
|
574
|
+
body = stripMentionQuotes(body);
|
|
563
575
|
const sendRes = await vaultApiFetch({
|
|
564
576
|
token,
|
|
565
577
|
path: `/v1/notify/channels/${encodeURIComponent(channel.channelId)}/messages`,
|
|
@@ -80,19 +80,5 @@ export interface RebootCronResult {
|
|
|
80
80
|
}
|
|
81
81
|
/** Drop any previously managed block, leaving every other entry untouched. */
|
|
82
82
|
export declare function stripManagedCron(existing: string): string;
|
|
83
|
-
/**
|
|
84
|
-
* Install the @reboot entry that brings the fallback supervisor back after a
|
|
85
|
-
* restart.
|
|
86
|
-
*
|
|
87
|
-
* Printing the line and trusting it to be pasted is what the installer used to
|
|
88
|
-
* do, and it is how the Muse pilot lost its messages: the host rebooted, the
|
|
89
|
-
* supervisor died with it, nothing restarted it, and the bot went quiet while
|
|
90
|
-
* every surface still reported healthy. A supervisor that does not survive a
|
|
91
|
-
* reboot is not a supervisor, so the entry is now written for real.
|
|
92
|
-
*
|
|
93
|
-
* Failure is NOT fatal — a host with no crontab is still a working kit until
|
|
94
|
-
* it restarts — but it is reported, so the caller can tell the operator the
|
|
95
|
-
* one thing they must then do by hand.
|
|
96
|
-
*/
|
|
97
83
|
export declare function ensureRebootCrontab(paths: Pick<AgentKitPaths, "agentDir" | "logsDir">, host: ServiceHostPaths, run?: CronRunner): RebootCronResult;
|
|
98
84
|
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -161,6 +161,10 @@ export function stripManagedCron(existing) {
|
|
|
161
161
|
* it restarts — but it is reported, so the caller can tell the operator the
|
|
162
162
|
* one thing they must then do by hand.
|
|
163
163
|
*/
|
|
164
|
+
/** No cron on the host: spawn failed outright, or the shell could not find it. */
|
|
165
|
+
function isMissingCrontab(r) {
|
|
166
|
+
return r.status === 127 || /\bENOENT\b|command not found|not found/i.test(r.stderr);
|
|
167
|
+
}
|
|
164
168
|
export function ensureRebootCrontab(paths, host, run) {
|
|
165
169
|
const exec = run ??
|
|
166
170
|
((args, input) => {
|
|
@@ -170,6 +174,17 @@ export function ensureRebootCrontab(paths, host, run) {
|
|
|
170
174
|
return { status: r.status ?? 1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
171
175
|
});
|
|
172
176
|
const listed = exec(["-l"]);
|
|
177
|
+
// A minimal host image often ships without cron at all, which is not an
|
|
178
|
+
// error the operator can act on unless it is named. Observed on the Muse
|
|
179
|
+
// pilot: the reboot entry could not be written because the cron package was
|
|
180
|
+
// simply not installed, and the raw ENOENT did not say so.
|
|
181
|
+
if (isMissingCrontab(listed)) {
|
|
182
|
+
return {
|
|
183
|
+
installed: false,
|
|
184
|
+
reason: "the `crontab` command is not installed on this host (install your distribution's cron package, " +
|
|
185
|
+
"for example `apt-get install -y cron` or `apk add busybox-cron`), then run `hq agent kit install` again",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
173
188
|
// An empty crontab exits non-zero with "no crontab for <user>"; that is not
|
|
174
189
|
// a failure, it just means there is nothing to preserve. Anything else is.
|
|
175
190
|
const noCrontabYet = listed.status !== 0 && /no crontab for/i.test(listed.stderr);
|