@indigoai-us/hq-cli 5.311.3 → 5.312.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 +16 -0
- package/dist/command-catalog.generated.d.ts +95 -0
- package/dist/command-catalog.generated.js +120 -0
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/cloud-provision.d.ts +3 -0
- package/dist/commands/cloud-provision.js +34 -1
- package/dist/commands/daemon.js +22 -0
- package/dist/commands/dm.js +22 -6
- package/dist/commands/files-browse.js +16 -4
- package/dist/commands/lanes-message.js +17 -2
- package/dist/commands/lanes-shared.d.ts +1 -0
- package/dist/commands/lanes.js +96 -20
- package/dist/commands/vault-retention-flag.d.ts +3 -0
- package/dist/commands/vault-retention-flag.js +9 -0
- package/dist/commands/vault-retention.d.ts +11 -0
- package/dist/commands/vault-retention.js +69 -0
- package/dist/lib/core-utils/sentry-report.js +21 -4
- package/dist/lib/daemon/catalog.d.ts +2 -0
- package/dist/lib/daemon/catalog.js +13 -1
- package/dist/lib/daemon/config.d.ts +2 -0
- package/dist/lib/daemon/config.js +4 -0
- package/dist/lib/daemon/run.d.ts +2 -0
- package/dist/lib/daemon/run.js +5 -0
- package/dist/lib/lanes/autopilot.d.ts +38 -1
- package/dist/lib/lanes/autopilot.js +353 -5
- package/dist/lib/lanes/flag-keys.d.ts +9 -1
- package/dist/lib/lanes/flag-keys.js +9 -1
- package/dist/lib/lanes/keep-alive.d.ts +2 -0
- package/dist/lib/lanes/keep-alive.js +2 -0
- package/dist/lib/lanes/reconcile.d.ts +5 -0
- package/dist/lib/lanes/reconcile.js +57 -4
- package/dist/lib/lanes/watcher-main.js +20 -1
- package/dist/lib/lanes/watcher.d.ts +9 -0
- package/dist/lib/lanes/watcher.js +47 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.312.0] — 2026-10-02
|
|
6
|
+
- The desktop app can turn event-driven daemon sync on or off with its Instant Sync setting. The daemon stores the preference and restarts when a running daemon needs to apply it.
|
|
7
|
+
- `hq dm channel` now explains empty channels and displays a readable fallback for bodyless system notifications.
|
|
8
|
+
|
|
9
|
+
- Personal vault browsing now treats slash and dot paths as the root and normalizes leading slashes on nested paths.
|
|
10
|
+
- Personal and company file browsing normalize root markers before listing.
|
|
11
|
+
- Schedule the first eligible lead keep-alive check from the lane terminal watcher when the opt-in flags are enabled.
|
|
12
|
+
- Add default-off KPI freshness reporting and deduplicated re-measurement nudges to lane autopilot.
|
|
13
|
+
- Add company-scoped Outpost owner lookup for hostless lane ULIDs used by `hq lanes message`.
|
|
14
|
+
- Add opt-in autopilot nudges and operator escalation for missed lead deadlines.
|
|
15
|
+
- Report the bounded host BASH_ENV class for hook telemetry so HQ-CLI-2C startup timing can be distinguished accurately.
|
|
16
|
+
- Add gated `hq vault retention get|set` commands for the per-vault default retention period.
|
|
17
|
+
|
|
18
|
+
## [5.311.4] — 2026-10-02
|
|
19
|
+
- Preserve existing local company starter files during cloud provisioning by having the CLI request that hq-pro skip blank starter seeding; older servers are detected before company creation.
|
|
20
|
+
|
|
5
21
|
## [5.311.3] — 2026-10-01
|
|
6
22
|
- Match company file access checks against the company-anchored paths printed by `hq files browse`.
|
|
7
23
|
- Record a placement resolved to the current host as local so the lane starts without dispatching back through Outpost pickup.
|
|
@@ -5071,6 +5071,49 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
5071
5071
|
readonly subcommands: readonly [];
|
|
5072
5072
|
}];
|
|
5073
5073
|
}];
|
|
5074
|
+
}, {
|
|
5075
|
+
readonly name: "vault";
|
|
5076
|
+
readonly description: "Manage HQ vault settings";
|
|
5077
|
+
readonly aliases: readonly [];
|
|
5078
|
+
readonly hidden: false;
|
|
5079
|
+
readonly usage: "[options] [command]";
|
|
5080
|
+
readonly arguments: readonly [];
|
|
5081
|
+
readonly options: readonly [];
|
|
5082
|
+
readonly subcommands: readonly [{
|
|
5083
|
+
readonly name: "retention";
|
|
5084
|
+
readonly description: "Get or set the default retention for future vault object versions";
|
|
5085
|
+
readonly aliases: readonly [];
|
|
5086
|
+
readonly hidden: false;
|
|
5087
|
+
readonly usage: "[options] [command]";
|
|
5088
|
+
readonly arguments: readonly [];
|
|
5089
|
+
readonly options: readonly [{
|
|
5090
|
+
readonly flags: "--company <slug>";
|
|
5091
|
+
readonly description: "Company vault slug (omit for your personal vault)";
|
|
5092
|
+
}];
|
|
5093
|
+
readonly subcommands: readonly [{
|
|
5094
|
+
readonly name: "get";
|
|
5095
|
+
readonly description: "Show the vault's default retention in days";
|
|
5096
|
+
readonly aliases: readonly [];
|
|
5097
|
+
readonly hidden: false;
|
|
5098
|
+
readonly usage: "[options]";
|
|
5099
|
+
readonly arguments: readonly [];
|
|
5100
|
+
readonly options: readonly [];
|
|
5101
|
+
readonly subcommands: readonly [];
|
|
5102
|
+
}, {
|
|
5103
|
+
readonly name: "set";
|
|
5104
|
+
readonly description: "Set the vault's default retention in days (1-30)";
|
|
5105
|
+
readonly aliases: readonly [];
|
|
5106
|
+
readonly hidden: false;
|
|
5107
|
+
readonly usage: "[options] <days>";
|
|
5108
|
+
readonly arguments: readonly [{
|
|
5109
|
+
readonly name: "days";
|
|
5110
|
+
readonly required: true;
|
|
5111
|
+
readonly variadic: false;
|
|
5112
|
+
}];
|
|
5113
|
+
readonly options: readonly [];
|
|
5114
|
+
readonly subcommands: readonly [];
|
|
5115
|
+
}];
|
|
5116
|
+
}];
|
|
5074
5117
|
}, {
|
|
5075
5118
|
readonly name: "core";
|
|
5076
5119
|
readonly description: "HQ scaffold scripts hosted by the CLI (core; not a stable surface)";
|
|
@@ -8630,6 +8673,9 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
8630
8673
|
readonly options: readonly [{
|
|
8631
8674
|
readonly flags: "--text <msg>";
|
|
8632
8675
|
readonly description: "Message text";
|
|
8676
|
+
}, {
|
|
8677
|
+
readonly flags: "--text-file <path>";
|
|
8678
|
+
readonly description: "Read message text from a file";
|
|
8633
8679
|
}, {
|
|
8634
8680
|
readonly flags: "--project <slug>";
|
|
8635
8681
|
readonly description: "Re-point project attribution; a changed story is named in the resumed prompt";
|
|
@@ -9292,6 +9338,30 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
9292
9338
|
}];
|
|
9293
9339
|
readonly subcommands: readonly [];
|
|
9294
9340
|
}];
|
|
9341
|
+
}, {
|
|
9342
|
+
readonly name: "_keep-alive-terminal-trigger";
|
|
9343
|
+
readonly description: "";
|
|
9344
|
+
readonly aliases: readonly [];
|
|
9345
|
+
readonly hidden: true;
|
|
9346
|
+
readonly usage: "[options] <laneId> <version> <dueAt>";
|
|
9347
|
+
readonly arguments: readonly [{
|
|
9348
|
+
readonly name: "laneId";
|
|
9349
|
+
readonly required: true;
|
|
9350
|
+
readonly variadic: false;
|
|
9351
|
+
}, {
|
|
9352
|
+
readonly name: "version";
|
|
9353
|
+
readonly required: true;
|
|
9354
|
+
readonly variadic: false;
|
|
9355
|
+
}, {
|
|
9356
|
+
readonly name: "dueAt";
|
|
9357
|
+
readonly required: true;
|
|
9358
|
+
readonly variadic: false;
|
|
9359
|
+
}];
|
|
9360
|
+
readonly options: readonly [{
|
|
9361
|
+
readonly flags: "--json";
|
|
9362
|
+
readonly description: "JSON on stdout (always)";
|
|
9363
|
+
}];
|
|
9364
|
+
readonly subcommands: readonly [];
|
|
9295
9365
|
}];
|
|
9296
9366
|
}, {
|
|
9297
9367
|
readonly name: "monitor";
|
|
@@ -9551,6 +9621,31 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
9551
9621
|
readonly description: "Print machine-readable JSON";
|
|
9552
9622
|
}];
|
|
9553
9623
|
readonly subcommands: readonly [];
|
|
9624
|
+
}, {
|
|
9625
|
+
readonly name: "instant-sync";
|
|
9626
|
+
readonly description: "Control daemon Instant Sync";
|
|
9627
|
+
readonly aliases: readonly [];
|
|
9628
|
+
readonly hidden: false;
|
|
9629
|
+
readonly usage: "[options] [command]";
|
|
9630
|
+
readonly arguments: readonly [];
|
|
9631
|
+
readonly options: readonly [];
|
|
9632
|
+
readonly subcommands: readonly [{
|
|
9633
|
+
readonly name: "set";
|
|
9634
|
+
readonly description: "Set whether daemon sync uses Instant Sync";
|
|
9635
|
+
readonly aliases: readonly [];
|
|
9636
|
+
readonly hidden: false;
|
|
9637
|
+
readonly usage: "[options] <state>";
|
|
9638
|
+
readonly arguments: readonly [{
|
|
9639
|
+
readonly name: "state";
|
|
9640
|
+
readonly required: true;
|
|
9641
|
+
readonly variadic: false;
|
|
9642
|
+
}];
|
|
9643
|
+
readonly options: readonly [{
|
|
9644
|
+
readonly flags: "--json";
|
|
9645
|
+
readonly description: "Print machine-readable JSON";
|
|
9646
|
+
}];
|
|
9647
|
+
readonly subcommands: readonly [];
|
|
9648
|
+
}];
|
|
9554
9649
|
}, {
|
|
9555
9650
|
readonly name: "pause";
|
|
9556
9651
|
readonly description: "Pause daemon sync until resumed";
|
|
@@ -6559,6 +6559,59 @@ export const COMMAND_CATALOG = [
|
|
|
6559
6559
|
}
|
|
6560
6560
|
]
|
|
6561
6561
|
},
|
|
6562
|
+
{
|
|
6563
|
+
"name": "vault",
|
|
6564
|
+
"description": "Manage HQ vault settings",
|
|
6565
|
+
"aliases": [],
|
|
6566
|
+
"hidden": false,
|
|
6567
|
+
"usage": "[options] [command]",
|
|
6568
|
+
"arguments": [],
|
|
6569
|
+
"options": [],
|
|
6570
|
+
"subcommands": [
|
|
6571
|
+
{
|
|
6572
|
+
"name": "retention",
|
|
6573
|
+
"description": "Get or set the default retention for future vault object versions",
|
|
6574
|
+
"aliases": [],
|
|
6575
|
+
"hidden": false,
|
|
6576
|
+
"usage": "[options] [command]",
|
|
6577
|
+
"arguments": [],
|
|
6578
|
+
"options": [
|
|
6579
|
+
{
|
|
6580
|
+
"flags": "--company <slug>",
|
|
6581
|
+
"description": "Company vault slug (omit for your personal vault)"
|
|
6582
|
+
}
|
|
6583
|
+
],
|
|
6584
|
+
"subcommands": [
|
|
6585
|
+
{
|
|
6586
|
+
"name": "get",
|
|
6587
|
+
"description": "Show the vault's default retention in days",
|
|
6588
|
+
"aliases": [],
|
|
6589
|
+
"hidden": false,
|
|
6590
|
+
"usage": "[options]",
|
|
6591
|
+
"arguments": [],
|
|
6592
|
+
"options": [],
|
|
6593
|
+
"subcommands": []
|
|
6594
|
+
},
|
|
6595
|
+
{
|
|
6596
|
+
"name": "set",
|
|
6597
|
+
"description": "Set the vault's default retention in days (1-30)",
|
|
6598
|
+
"aliases": [],
|
|
6599
|
+
"hidden": false,
|
|
6600
|
+
"usage": "[options] <days>",
|
|
6601
|
+
"arguments": [
|
|
6602
|
+
{
|
|
6603
|
+
"name": "days",
|
|
6604
|
+
"required": true,
|
|
6605
|
+
"variadic": false
|
|
6606
|
+
}
|
|
6607
|
+
],
|
|
6608
|
+
"options": [],
|
|
6609
|
+
"subcommands": []
|
|
6610
|
+
}
|
|
6611
|
+
]
|
|
6612
|
+
}
|
|
6613
|
+
]
|
|
6614
|
+
},
|
|
6562
6615
|
{
|
|
6563
6616
|
"name": "core",
|
|
6564
6617
|
"description": "HQ scaffold scripts hosted by the CLI (core; not a stable surface)",
|
|
@@ -11124,6 +11177,10 @@ export const COMMAND_CATALOG = [
|
|
|
11124
11177
|
"flags": "--text <msg>",
|
|
11125
11178
|
"description": "Message text"
|
|
11126
11179
|
},
|
|
11180
|
+
{
|
|
11181
|
+
"flags": "--text-file <path>",
|
|
11182
|
+
"description": "Read message text from a file"
|
|
11183
|
+
},
|
|
11127
11184
|
{
|
|
11128
11185
|
"flags": "--project <slug>",
|
|
11129
11186
|
"description": "Re-point project attribution; a changed story is named in the resumed prompt"
|
|
@@ -11986,6 +12043,37 @@ export const COMMAND_CATALOG = [
|
|
|
11986
12043
|
"subcommands": []
|
|
11987
12044
|
}
|
|
11988
12045
|
]
|
|
12046
|
+
},
|
|
12047
|
+
{
|
|
12048
|
+
"name": "_keep-alive-terminal-trigger",
|
|
12049
|
+
"description": "",
|
|
12050
|
+
"aliases": [],
|
|
12051
|
+
"hidden": true,
|
|
12052
|
+
"usage": "[options] <laneId> <version> <dueAt>",
|
|
12053
|
+
"arguments": [
|
|
12054
|
+
{
|
|
12055
|
+
"name": "laneId",
|
|
12056
|
+
"required": true,
|
|
12057
|
+
"variadic": false
|
|
12058
|
+
},
|
|
12059
|
+
{
|
|
12060
|
+
"name": "version",
|
|
12061
|
+
"required": true,
|
|
12062
|
+
"variadic": false
|
|
12063
|
+
},
|
|
12064
|
+
{
|
|
12065
|
+
"name": "dueAt",
|
|
12066
|
+
"required": true,
|
|
12067
|
+
"variadic": false
|
|
12068
|
+
}
|
|
12069
|
+
],
|
|
12070
|
+
"options": [
|
|
12071
|
+
{
|
|
12072
|
+
"flags": "--json",
|
|
12073
|
+
"description": "JSON on stdout (always)"
|
|
12074
|
+
}
|
|
12075
|
+
],
|
|
12076
|
+
"subcommands": []
|
|
11989
12077
|
}
|
|
11990
12078
|
]
|
|
11991
12079
|
},
|
|
@@ -12314,6 +12402,38 @@ export const COMMAND_CATALOG = [
|
|
|
12314
12402
|
],
|
|
12315
12403
|
"subcommands": []
|
|
12316
12404
|
},
|
|
12405
|
+
{
|
|
12406
|
+
"name": "instant-sync",
|
|
12407
|
+
"description": "Control daemon Instant Sync",
|
|
12408
|
+
"aliases": [],
|
|
12409
|
+
"hidden": false,
|
|
12410
|
+
"usage": "[options] [command]",
|
|
12411
|
+
"arguments": [],
|
|
12412
|
+
"options": [],
|
|
12413
|
+
"subcommands": [
|
|
12414
|
+
{
|
|
12415
|
+
"name": "set",
|
|
12416
|
+
"description": "Set whether daemon sync uses Instant Sync",
|
|
12417
|
+
"aliases": [],
|
|
12418
|
+
"hidden": false,
|
|
12419
|
+
"usage": "[options] <state>",
|
|
12420
|
+
"arguments": [
|
|
12421
|
+
{
|
|
12422
|
+
"name": "state",
|
|
12423
|
+
"required": true,
|
|
12424
|
+
"variadic": false
|
|
12425
|
+
}
|
|
12426
|
+
],
|
|
12427
|
+
"options": [
|
|
12428
|
+
{
|
|
12429
|
+
"flags": "--json",
|
|
12430
|
+
"description": "Print machine-readable JSON"
|
|
12431
|
+
}
|
|
12432
|
+
],
|
|
12433
|
+
"subcommands": []
|
|
12434
|
+
}
|
|
12435
|
+
]
|
|
12436
|
+
},
|
|
12317
12437
|
{
|
|
12318
12438
|
"name": "pause",
|
|
12319
12439
|
"description": "Pause daemon sync until resumed",
|
|
@@ -370,6 +370,12 @@ export declare const REGISTRATION_PLAN: readonly [{
|
|
|
370
370
|
readonly parent: "program";
|
|
371
371
|
readonly module: "./commands/billing.js";
|
|
372
372
|
readonly exportName: "registerBillingCommand";
|
|
373
|
+
}, {
|
|
374
|
+
readonly type: "registrar";
|
|
375
|
+
readonly root: "vault";
|
|
376
|
+
readonly parent: "program";
|
|
377
|
+
readonly module: "./commands/vault-retention.js";
|
|
378
|
+
readonly exportName: "registerVaultRetentionCommand";
|
|
373
379
|
}, {
|
|
374
380
|
readonly type: "registrar";
|
|
375
381
|
readonly root: "core";
|
|
@@ -64,6 +64,7 @@ export const REGISTRATION_PLAN = [
|
|
|
64
64
|
{ type: "registrar", root: "agent", parent: "program", module: "./commands/agent.js", exportName: "registerAgentCommand" },
|
|
65
65
|
{ type: "registrar", root: "outposts", parent: "program", module: "./commands/outposts.js", exportName: "registerOutpostsCommand" },
|
|
66
66
|
{ type: "registrar", root: "billing", parent: "program", module: "./commands/billing.js", exportName: "registerBillingCommand" },
|
|
67
|
+
{ type: "registrar", root: "vault", parent: "program", module: "./commands/vault-retention.js", exportName: "registerVaultRetentionCommand" },
|
|
67
68
|
{ type: "registrar", root: "core", parent: "program", module: "./commands/core.js", exportName: "registerCoreCommands" },
|
|
68
69
|
{ type: "registrar", root: "search", parent: "program", module: "./commands/search.js", exportName: "registerSearchCommand" },
|
|
69
70
|
{ type: "registrar", root: "index", parent: "program", module: "./commands/index-cmd.js", exportName: "registerIndexCommand" },
|
|
@@ -135,6 +135,8 @@ export interface VaultClient {
|
|
|
135
135
|
checkSlugInMyNamespace(slug: string): Promise<{
|
|
136
136
|
available: boolean;
|
|
137
137
|
conflictingCompanyUid?: string;
|
|
138
|
+
/** Older hq-pro servers omit this field; absence means unsupported. */
|
|
139
|
+
skipStarterSeedSupported?: boolean;
|
|
138
140
|
}>;
|
|
139
141
|
/** Fetch a company entity by uid. Used to materialize the entity
|
|
140
142
|
* after `checkSlugInMyNamespace` reports a same-namespace collision. */
|
|
@@ -143,6 +145,7 @@ export interface VaultClient {
|
|
|
143
145
|
slug: string;
|
|
144
146
|
name: string;
|
|
145
147
|
ownerUid?: string;
|
|
148
|
+
skipStarterSeed?: boolean;
|
|
146
149
|
}): Promise<VaultEntity>;
|
|
147
150
|
/**
|
|
148
151
|
* Soft-tombstone a company (`DELETE /entity/{uid}`). Owner-only. Leaves the
|
|
@@ -34,6 +34,25 @@ import * as yaml from "js-yaml";
|
|
|
34
34
|
import { parseUserYaml } from "../utils/user-yaml-error.js";
|
|
35
35
|
import { createSyncProgressRecorder, share, } from "@indigoai-us/hq-cloud";
|
|
36
36
|
import { DEFAULT_HQ_ROOT, DEFAULT_VAULT_API_URL, ensureCognitoToken, buildVaultConfig, } from "../utils/cognito-session.js";
|
|
37
|
+
const SHARED_COMPANY_STARTER_PATHS = [
|
|
38
|
+
"README.md",
|
|
39
|
+
"board.json",
|
|
40
|
+
"knowledge/README.md",
|
|
41
|
+
"knowledge/design-styles/packs/.gitkeep",
|
|
42
|
+
"people/.gitkeep",
|
|
43
|
+
"policies/.gitkeep",
|
|
44
|
+
"projects/.gitkeep",
|
|
45
|
+
"repos/.gitkeep",
|
|
46
|
+
"skills/.gitkeep",
|
|
47
|
+
"sources/meetings/source.yaml",
|
|
48
|
+
"workspace/.gitignore",
|
|
49
|
+
"workspace/index.jsonl",
|
|
50
|
+
"workspace/sessions/.gitkeep",
|
|
51
|
+
];
|
|
52
|
+
function hasLocalSharedStarterPath(hqRoot, slug) {
|
|
53
|
+
const companyDir = path.join(hqRoot, "companies", slug);
|
|
54
|
+
return SHARED_COMPANY_STARTER_PATHS.some((relativePath) => fs.existsSync(path.join(companyDir, relativePath)));
|
|
55
|
+
}
|
|
37
56
|
/** Custom error class so the CLI runner can map to exit codes. */
|
|
38
57
|
export class ProvisionError extends Error {
|
|
39
58
|
code;
|
|
@@ -333,7 +352,11 @@ export function createDefaultVaultClient(apiUrl, accessToken) {
|
|
|
333
352
|
const body = await safeBody(res);
|
|
334
353
|
throw new ProvisionError(1, `Vault GET /entity/check-slug/me failed: ${res.status} ${res.statusText} — ${body}`);
|
|
335
354
|
}
|
|
336
|
-
|
|
355
|
+
const data = (await res.json());
|
|
356
|
+
return {
|
|
357
|
+
...data,
|
|
358
|
+
skipStarterSeedSupported: data.skipStarterSeedSupported === true,
|
|
359
|
+
};
|
|
337
360
|
});
|
|
338
361
|
},
|
|
339
362
|
async getCompanyByUid(uid) {
|
|
@@ -359,6 +382,8 @@ export function createDefaultVaultClient(apiUrl, accessToken) {
|
|
|
359
382
|
};
|
|
360
383
|
if (input.ownerUid)
|
|
361
384
|
body.ownerUid = input.ownerUid;
|
|
385
|
+
if (input.skipStarterSeed)
|
|
386
|
+
body.skipStarterSeed = true;
|
|
362
387
|
return withVaultRequest(url, {
|
|
363
388
|
method: "POST",
|
|
364
389
|
headers,
|
|
@@ -548,6 +573,7 @@ export async function provisionCompany(options) {
|
|
|
548
573
|
// match the caller's own person UID(s) from `listMyPersonEntities`.
|
|
549
574
|
const callerOwnedUids = new Set(persons.map((p) => p.uid));
|
|
550
575
|
const callerIsOwner = !options.ownerUid || callerOwnedUids.has(options.ownerUid);
|
|
576
|
+
const hasLocalSharedStarterFiles = hasLocalSharedStarterPath(options.hqRoot, options.slug);
|
|
551
577
|
let entity;
|
|
552
578
|
let createdEntity = false;
|
|
553
579
|
if (callerIsOwner) {
|
|
@@ -557,17 +583,24 @@ export async function provisionCompany(options) {
|
|
|
557
583
|
entity = await vaultClient.getCompanyByUid(slugCheck.conflictingCompanyUid);
|
|
558
584
|
}
|
|
559
585
|
else {
|
|
586
|
+
if (hasLocalSharedStarterFiles && !slugCheck.skipStarterSeedSupported) {
|
|
587
|
+
throw new ProvisionError(1, "This hq-pro server does not support preserving local starter files during cloud provisioning; update hq-pro and retry");
|
|
588
|
+
}
|
|
560
589
|
log(`slug available in caller's namespace — creating vault entity`);
|
|
561
590
|
entity = await vaultClient.createCompanyEntity({
|
|
562
591
|
slug: options.slug,
|
|
563
592
|
name: options.name ?? options.slug,
|
|
564
593
|
ownerUid: options.ownerUid,
|
|
594
|
+
...(hasLocalSharedStarterFiles ? { skipStarterSeed: true } : {}),
|
|
565
595
|
});
|
|
566
596
|
createdEntity = true;
|
|
567
597
|
log(`created vault entity uid=${entity.uid}`);
|
|
568
598
|
}
|
|
569
599
|
}
|
|
570
600
|
else {
|
|
601
|
+
if (hasLocalSharedStarterFiles) {
|
|
602
|
+
throw new ProvisionError(1, "Cannot preserve local starter files when provisioning for a different owner");
|
|
603
|
+
}
|
|
571
604
|
log(`--owner ${options.ownerUid} differs from caller's person(s); skipping namespace pre-check (server authoritatively gates per-target-namespace)`);
|
|
572
605
|
entity = await vaultClient.createCompanyEntity({
|
|
573
606
|
slug: options.slug,
|
package/dist/commands/daemon.js
CHANGED
|
@@ -474,6 +474,18 @@ async function runDaemonSyncNow(d, opts) {
|
|
|
474
474
|
const result = { accepted: true, action: "restart-sync-runner", daemonRunning: running };
|
|
475
475
|
d.out(opts.json ? JSON.stringify(result, null, 2) : running ? "Asked the daemon to start a fresh sync pass." : "Queued a fresh sync pass for the next daemon start.");
|
|
476
476
|
}
|
|
477
|
+
async function runDaemonSyncInstantSyncSet(d, enabled, opts) {
|
|
478
|
+
const host = currentHost(d);
|
|
479
|
+
const config = readDaemonConfig(host.paths.configPath);
|
|
480
|
+
const changed = config.instantSyncEnabled !== enabled;
|
|
481
|
+
if (changed)
|
|
482
|
+
writeDaemonConfig(host.paths.configPath, { ...config, instantSyncEnabled: enabled });
|
|
483
|
+
const daemonRunning = runningDaemonPid(host.paths.lockPath, d.daemonAlive) !== null;
|
|
484
|
+
if (changed && daemonRunning)
|
|
485
|
+
queueDaemonSyncControl(d, { action: "restart-daemon" });
|
|
486
|
+
const result = { instantSyncEnabled: enabled, persisted: true, daemonRestartRequested: changed && daemonRunning };
|
|
487
|
+
d.out(opts.json ? JSON.stringify(result, null, 2) : `Instant Sync ${enabled ? "enabled" : "disabled"}${daemonRunning ? "; asked the daemon to restart" : "; saved for the next daemon start"}.`);
|
|
488
|
+
}
|
|
477
489
|
async function runDaemonSyncPause(d, paused, opts) {
|
|
478
490
|
const host = currentHost(d);
|
|
479
491
|
const config = readDaemonConfig(host.paths.configPath);
|
|
@@ -772,6 +784,16 @@ export function registerDaemonCommand(program, over = {}) {
|
|
|
772
784
|
.description("Start a fresh sync pass immediately")
|
|
773
785
|
.option("--json", "Print machine-readable JSON")
|
|
774
786
|
.action(wrap((d, opts) => runDaemonSyncNow(d, opts)));
|
|
787
|
+
const instantSync = sync.command("instant-sync").description("Control daemon Instant Sync");
|
|
788
|
+
instantSync
|
|
789
|
+
.command("set <state>")
|
|
790
|
+
.description("Set whether daemon sync uses Instant Sync")
|
|
791
|
+
.option("--json", "Print machine-readable JSON")
|
|
792
|
+
.action(wrap((d, state, opts) => {
|
|
793
|
+
if (state !== "on" && state !== "off")
|
|
794
|
+
throw new DaemonCommandError("Use on or off for Instant Sync.");
|
|
795
|
+
return runDaemonSyncInstantSyncSet(d, state === "on", opts);
|
|
796
|
+
}));
|
|
775
797
|
sync
|
|
776
798
|
.command("pause")
|
|
777
799
|
.description("Pause daemon sync until resumed")
|
package/dist/commands/dm.js
CHANGED
|
@@ -872,7 +872,12 @@ export function formatChannelMessage(m, nowMs, opts = {}) {
|
|
|
872
872
|
const id = opts.showEventId ? (m.eventId ?? m.messageId) : undefined;
|
|
873
873
|
const idPart = id ? ` ${chalk.dim(id)}` : "";
|
|
874
874
|
const bodyIndent = `${indent} `;
|
|
875
|
-
|
|
875
|
+
const body = firstLine(m.body);
|
|
876
|
+
const readableBody = body ||
|
|
877
|
+
(senderLabel(m).toLowerCase() === "hq"
|
|
878
|
+
? "System notification (no text provided)."
|
|
879
|
+
: "(no message text)");
|
|
880
|
+
return `${indent}${who} ${when}${idPart}\n${bodyIndent}${readableBody}${formatAttachments(m, bodyIndent)}`;
|
|
876
881
|
}
|
|
877
882
|
/**
|
|
878
883
|
* Read the caller's person/agent uid and email from a JWT we already hold.
|
|
@@ -1227,26 +1232,27 @@ async function runDmThread(identifier, opts) {
|
|
|
1227
1232
|
* `hq channels`). Throws a user-facing message when nothing matches or a name is
|
|
1228
1233
|
* ambiguous.
|
|
1229
1234
|
*/
|
|
1230
|
-
async function
|
|
1235
|
+
async function resolveChannel(token, target) {
|
|
1231
1236
|
const raw = target.trim().replace(/^#/, "");
|
|
1232
1237
|
if (!raw)
|
|
1233
1238
|
throw new Error("A channel name or id is required.");
|
|
1234
1239
|
const channels = await fetchChannels(token);
|
|
1235
1240
|
const named = matchChannelsByName(channels, raw);
|
|
1236
1241
|
if (named.length === 1)
|
|
1237
|
-
return named[0]
|
|
1242
|
+
return named[0];
|
|
1238
1243
|
if (named.length > 1) {
|
|
1239
1244
|
throw new Error(`'${raw}' matches ${named.length} channels — pass the channel id instead (see \`hq channels\`).`);
|
|
1240
1245
|
}
|
|
1241
1246
|
const byId = channels.find((c) => c.channelId === raw);
|
|
1242
1247
|
if (byId)
|
|
1243
|
-
return byId
|
|
1248
|
+
return byId;
|
|
1244
1249
|
throw new Error(`No channel named or with id '${raw}' — run \`hq channels\` to see yours.`);
|
|
1245
1250
|
}
|
|
1246
1251
|
async function runDmChannel(target, opts) {
|
|
1247
1252
|
try {
|
|
1248
1253
|
const token = await ensureCognitoToken();
|
|
1249
|
-
const
|
|
1254
|
+
const channel = await resolveChannel(token, target);
|
|
1255
|
+
const channelId = channel.channelId;
|
|
1250
1256
|
const query = {};
|
|
1251
1257
|
if (opts.limit)
|
|
1252
1258
|
query.limit = opts.limit;
|
|
@@ -1268,7 +1274,17 @@ async function runDmChannel(target, opts) {
|
|
|
1268
1274
|
console.log(JSON.stringify(ordered, null, 2));
|
|
1269
1275
|
}
|
|
1270
1276
|
else if (ordered.length === 0) {
|
|
1271
|
-
|
|
1277
|
+
const channelType = channel.scope === "personal"
|
|
1278
|
+
? "personal"
|
|
1279
|
+
: channel.scope === "group"
|
|
1280
|
+
? "group"
|
|
1281
|
+
: "shared";
|
|
1282
|
+
const channelName = channel.name?.trim() || channel.slug?.trim() || channelId;
|
|
1283
|
+
const requestedTarget = target.trim().replace(/^#/, "");
|
|
1284
|
+
const sendTarget = requestedTarget === channelId
|
|
1285
|
+
? channelId
|
|
1286
|
+
: channel.slug?.trim() || requestedTarget;
|
|
1287
|
+
console.log(chalk.dim(`No messages in ${channelType} channel "${channelName}" yet.\nSend a message with: hq dm ${sendTarget} "Hello"`));
|
|
1272
1288
|
}
|
|
1273
1289
|
else {
|
|
1274
1290
|
console.log(chalk.green(`${ordered.length} message${ordered.length === 1 ? "" : "s"}:`));
|
|
@@ -63,6 +63,11 @@ export function parseCompanySlugFromPath(prefix) {
|
|
|
63
63
|
}
|
|
64
64
|
return parts[1];
|
|
65
65
|
}
|
|
66
|
+
// Normalize personal paths before using them as S3 list prefixes.
|
|
67
|
+
function normalizePersonalBrowsePrefix(pathPrefix) {
|
|
68
|
+
const normalized = pathPrefix.replace(/^\/+/, "");
|
|
69
|
+
return /^[/.]+$/.test(normalized) ? "" : normalized;
|
|
70
|
+
}
|
|
66
71
|
/**
|
|
67
72
|
* Translate a CLI `companies/<slug>/…` path into the company-relative S3 key
|
|
68
73
|
* the vault bucket actually stores. Company vault buckets are already
|
|
@@ -277,10 +282,17 @@ export async function runBrowse(input) {
|
|
|
277
282
|
}
|
|
278
283
|
// Company vault keys are company-relative (no `companies/<slug>/` prefix), so
|
|
279
284
|
// translate the CLI's anchored prefix into the bucket-relative form before
|
|
280
|
-
// listing. Personal-mode paths are
|
|
281
|
-
const
|
|
282
|
-
? pathPrefix
|
|
283
|
-
:
|
|
285
|
+
// listing. Personal-mode paths are normalized in bucket-relative form.
|
|
286
|
+
const bucketPrefix = personalMode
|
|
287
|
+
? normalizePersonalBrowsePrefix(pathPrefix)
|
|
288
|
+
: slug === undefined
|
|
289
|
+
? pathPrefix
|
|
290
|
+
: toBucketRelative(pathPrefix, slug);
|
|
291
|
+
// Company callers can spell the bucket root as `/` or `.`. S3's empty
|
|
292
|
+
// Prefix is the root; passing either literal would list under that key.
|
|
293
|
+
const listPrefix = !personalMode && (bucketPrefix === "/" || bucketPrefix === ".")
|
|
294
|
+
? ""
|
|
295
|
+
: bucketPrefix;
|
|
284
296
|
// Pull the caller's explicit-grant graph once so per-key classification is
|
|
285
297
|
// O(grants) without N round-trips. Retain the raw pattern after stripping
|
|
286
298
|
// only the redundant company anchor: `foo/` and `foo/*` have different
|
|
@@ -220,9 +220,23 @@ export function createLaneMessageHandlers(deps) {
|
|
|
220
220
|
storySwitchDeliveryStarted = true;
|
|
221
221
|
opts.onAccepted?.();
|
|
222
222
|
};
|
|
223
|
+
if (opts.text !== undefined && opts.textFile !== undefined) {
|
|
224
|
+
throw new LanesError("message_text_source_conflict", "Pass either --text or --text-file, not both", { lane_id: laneId });
|
|
225
|
+
}
|
|
223
226
|
let text = opts.text;
|
|
224
|
-
if (
|
|
225
|
-
|
|
227
|
+
if (opts.textFile !== undefined) {
|
|
228
|
+
const textPath = path.isAbsolute(opts.textFile)
|
|
229
|
+
? opts.textFile
|
|
230
|
+
: path.resolve(ctx.callerCwd(), opts.textFile);
|
|
231
|
+
try {
|
|
232
|
+
text = ctx.readFileSync(textPath, "utf8");
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new LanesError("message_text_file_unreadable", `Could not read message text file ${opts.textFile}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: laneId, path: textPath });
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (text === undefined || text.length === 0) {
|
|
239
|
+
throw new LanesError("missing_text", "Pass non-empty message text with --text or --text-file", { lane_id: laneId });
|
|
226
240
|
}
|
|
227
241
|
let resolution;
|
|
228
242
|
try {
|
|
@@ -261,6 +275,7 @@ export function createLaneMessageHandlers(deps) {
|
|
|
261
275
|
: undefined;
|
|
262
276
|
await dispatchOutpostLaneCommand(ctx, lane, "message", {
|
|
263
277
|
...opts,
|
|
278
|
+
text,
|
|
264
279
|
...(supersededMessageCallerId === undefined ? {} : { supersededMessageCallerId }),
|
|
265
280
|
...(supersededMessageSessionId === undefined ? {} : { supersededMessageSessionId }),
|
|
266
281
|
}, {
|