@indigoai-us/hq-cli 5.122.2 → 5.123.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 +38 -0
- package/README.md +46 -0
- package/dist/command-catalog.generated.d.ts +6 -3
- package/dist/command-catalog.generated.js +7 -3
- package/dist/commands/dm.d.ts +54 -1
- package/dist/commands/dm.js +204 -8
- package/dist/commands/whoami.d.ts +2 -2
- package/dist/commands/whoami.js +18 -19
- package/dist/lib/billing/plan-lock.d.ts +64 -69
- package/dist/lib/billing/plan-lock.js +230 -161
- package/dist/lib/mesh/api.js +96 -28
- package/dist/lib/mesh/live/daemon/board-refresh.d.ts +4 -1
- package/dist/lib/mesh/live/daemon/board-refresh.js +71 -30
- package/dist/lib/mesh/live/daemon/credentials.d.ts +6 -1
- package/dist/lib/mesh/live/daemon/credentials.js +20 -4
- package/dist/lib/mesh/live/daemon/doctor.d.ts +2 -0
- package/dist/lib/mesh/live/daemon/doctor.js +12 -0
- package/dist/lib/mesh/live/daemon/run.js +6 -1
- package/dist/lib/mesh/live/daemon/state.d.ts +3 -0
- package/dist/utils/cli-telemetry.d.ts +21 -0
- package/dist/utils/cli-telemetry.js +77 -1
- package/package.json +3 -2
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Refresh ~/.hq/work-context/sessions/<sid>/board.md for bound sessions
|
|
3
|
-
* from
|
|
3
|
+
* from the ProjectView read route at most every 60s.
|
|
4
|
+
*
|
|
5
|
+
* Board JSON remains the fallback for older projects, but story status is
|
|
6
|
+
* authoritative in ProjectView because `hq mesh story` writes there.
|
|
4
7
|
* Metadata-only snapshot — never prompts or tokens.
|
|
5
8
|
*/
|
|
6
9
|
export declare const BOARD_REFRESH_INTERVAL_MS = 60000;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Refresh ~/.hq/work-context/sessions/<sid>/board.md for bound sessions
|
|
3
|
-
* from
|
|
3
|
+
* from the ProjectView read route at most every 60s.
|
|
4
|
+
*
|
|
5
|
+
* Board JSON remains the fallback for older projects, but story status is
|
|
6
|
+
* authoritative in ProjectView because `hq mesh story` writes there.
|
|
4
7
|
* Metadata-only snapshot — never prompts or tokens.
|
|
5
8
|
*/
|
|
6
9
|
import * as fs from "node:fs";
|
|
@@ -115,9 +118,72 @@ export async function refreshBoundSessionBoards(deps) {
|
|
|
115
118
|
}
|
|
116
119
|
return { refreshed, skipped, errors };
|
|
117
120
|
}
|
|
121
|
+
function storiesFromProject(project) {
|
|
122
|
+
const storiesRaw = Array.isArray(project.stories) ? project.stories : [];
|
|
123
|
+
const stories = [];
|
|
124
|
+
for (const s of storiesRaw) {
|
|
125
|
+
if (!s || typeof s !== "object" || Array.isArray(s))
|
|
126
|
+
continue;
|
|
127
|
+
const row = s;
|
|
128
|
+
const id = typeof row.id === "string"
|
|
129
|
+
? row.id
|
|
130
|
+
: typeof row.storyId === "string"
|
|
131
|
+
? row.storyId
|
|
132
|
+
: typeof row.taskId === "string"
|
|
133
|
+
? row.taskId
|
|
134
|
+
: "";
|
|
135
|
+
if (!id)
|
|
136
|
+
continue;
|
|
137
|
+
stories.push({
|
|
138
|
+
id,
|
|
139
|
+
title: typeof row.title === "string"
|
|
140
|
+
? row.title.slice(0, 120)
|
|
141
|
+
: typeof row.name === "string"
|
|
142
|
+
? row.name.slice(0, 120)
|
|
143
|
+
: undefined,
|
|
144
|
+
status: typeof row.status === "string" ? row.status : undefined,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return stories;
|
|
148
|
+
}
|
|
149
|
+
function projectViewSnapshot(raw, companyUid, projectId) {
|
|
150
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
151
|
+
return null;
|
|
152
|
+
const project = raw;
|
|
153
|
+
if (project.companyUid !== companyUid || project.projectId !== projectId)
|
|
154
|
+
return null;
|
|
155
|
+
return {
|
|
156
|
+
projectId,
|
|
157
|
+
projectName: typeof project.name === "string"
|
|
158
|
+
? project.name
|
|
159
|
+
: typeof project.title === "string"
|
|
160
|
+
? project.title
|
|
161
|
+
: undefined,
|
|
162
|
+
stories: storiesFromProject(project),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function isMissingProject(error) {
|
|
166
|
+
// hq-pro's GET ProjectView handler emits PROJECT_VIEW_NOT_FOUND; retain the
|
|
167
|
+
// legacy PROJECT_NOT_FOUND code for compatibility with older producers.
|
|
168
|
+
return (error instanceof Error &&
|
|
169
|
+
/^404\s+(?:PROJECT_VIEW_NOT_FOUND|PROJECT_NOT_FOUND)\b/.test(error.message));
|
|
170
|
+
}
|
|
118
171
|
/** Build a BoardReader from meshJson-compatible fetch. */
|
|
119
172
|
export function createVaultBoardReader(opts) {
|
|
120
173
|
return async (companyUid, projectId) => {
|
|
174
|
+
// ProjectView is the live story-status source. Only use board.json when
|
|
175
|
+
// this project has not been migrated into ProjectView yet.
|
|
176
|
+
try {
|
|
177
|
+
const view = await opts.getJson(`/v1/work-mesh/projects/${encodeURIComponent(projectId)}?companyUid=${encodeURIComponent(companyUid)}`);
|
|
178
|
+
const snapshot = projectViewSnapshot(view, companyUid, projectId);
|
|
179
|
+
if (snapshot)
|
|
180
|
+
return snapshot;
|
|
181
|
+
throw new Error("ProjectView response did not match the requested company and project");
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
if (!isMissingProject(error))
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
121
187
|
const raw = await opts.getJson(`/companies/${encodeURIComponent(companyUid)}/board`);
|
|
122
188
|
if (!raw || typeof raw !== "object")
|
|
123
189
|
return null;
|
|
@@ -140,35 +206,10 @@ export function createVaultBoardReader(opts) {
|
|
|
140
206
|
const id = typeof p.id === "string" ? p.id : typeof p.projectId === "string" ? p.projectId : "";
|
|
141
207
|
if (id !== projectId)
|
|
142
208
|
continue;
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
: Array.isArray(p.tasks
|
|
146
|
-
|
|
147
|
-
: [];
|
|
148
|
-
const stories = [];
|
|
149
|
-
for (const s of storiesRaw) {
|
|
150
|
-
if (!s || typeof s !== "object" || Array.isArray(s))
|
|
151
|
-
continue;
|
|
152
|
-
const row = s;
|
|
153
|
-
const sid = typeof row.id === "string"
|
|
154
|
-
? row.id
|
|
155
|
-
: typeof row.storyId === "string"
|
|
156
|
-
? row.storyId
|
|
157
|
-
: typeof row.taskId === "string"
|
|
158
|
-
? row.taskId
|
|
159
|
-
: "";
|
|
160
|
-
if (!sid)
|
|
161
|
-
continue;
|
|
162
|
-
stories.push({
|
|
163
|
-
id: sid,
|
|
164
|
-
title: typeof row.title === "string"
|
|
165
|
-
? row.title.slice(0, 120)
|
|
166
|
-
: typeof row.name === "string"
|
|
167
|
-
? row.name.slice(0, 120)
|
|
168
|
-
: undefined,
|
|
169
|
-
status: typeof row.status === "string" ? row.status : undefined,
|
|
170
|
-
});
|
|
171
|
-
}
|
|
209
|
+
const stories = storiesFromProject({
|
|
210
|
+
...p,
|
|
211
|
+
stories: Array.isArray(p.stories) ? p.stories : p.tasks,
|
|
212
|
+
});
|
|
172
213
|
return {
|
|
173
214
|
projectId,
|
|
174
215
|
projectName: typeof p.name === "string"
|
|
@@ -49,6 +49,11 @@ export interface PresenceCompany {
|
|
|
49
49
|
companyUid: string;
|
|
50
50
|
presenceTopic: string;
|
|
51
51
|
}
|
|
52
|
+
export interface DroppedPresenceCompany {
|
|
53
|
+
companyUid: string;
|
|
54
|
+
/** Server-supplied cause when available; otherwise a deliberately neutral label. */
|
|
55
|
+
reason: string;
|
|
56
|
+
}
|
|
52
57
|
export interface Contract3Bundle {
|
|
53
58
|
contractVersion: 3;
|
|
54
59
|
credentials: IotCredentials;
|
|
@@ -58,7 +63,7 @@ export interface Contract3Bundle {
|
|
|
58
63
|
actorUid: string;
|
|
59
64
|
actorType: "human" | "agent";
|
|
60
65
|
companies: PresenceCompany[];
|
|
61
|
-
droppedCompanies:
|
|
66
|
+
droppedCompanies: DroppedPresenceCompany[];
|
|
62
67
|
expiresAt: string;
|
|
63
68
|
}
|
|
64
69
|
export type CredentialsFetcher = () => Promise<Contract3Bundle>;
|
|
@@ -229,9 +229,25 @@ export function normalizeContract3Bundle(raw) {
|
|
|
229
229
|
if (!expiresAt) {
|
|
230
230
|
throw new Error("contract-3 vend missing expiresAt");
|
|
231
231
|
}
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
232
|
+
const droppedCompanies = [];
|
|
233
|
+
for (const item of Array.isArray(r.droppedCompanies) ? r.droppedCompanies : []) {
|
|
234
|
+
if (typeof item === "string" && item.trim()) {
|
|
235
|
+
droppedCompanies.push({ companyUid: item, reason: "unknown" });
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (!item || typeof item !== "object" || Array.isArray(item))
|
|
239
|
+
continue;
|
|
240
|
+
const row = item;
|
|
241
|
+
const companyUid = typeof row.companyUid === "string" ? row.companyUid.trim() : "";
|
|
242
|
+
if (!companyUid)
|
|
243
|
+
continue;
|
|
244
|
+
droppedCompanies.push({
|
|
245
|
+
companyUid,
|
|
246
|
+
reason: typeof row.reason === "string" && row.reason.trim()
|
|
247
|
+
? row.reason.trim()
|
|
248
|
+
: "not included",
|
|
249
|
+
});
|
|
250
|
+
}
|
|
235
251
|
return {
|
|
236
252
|
contractVersion: 3,
|
|
237
253
|
credentials: {
|
|
@@ -246,7 +262,7 @@ export function normalizeContract3Bundle(raw) {
|
|
|
246
262
|
actorUid,
|
|
247
263
|
actorType,
|
|
248
264
|
companies,
|
|
249
|
-
droppedCompanies
|
|
265
|
+
droppedCompanies,
|
|
250
266
|
expiresAt,
|
|
251
267
|
};
|
|
252
268
|
}
|
|
@@ -32,6 +32,8 @@ export interface DaemonDoctorReport {
|
|
|
32
32
|
actorKind: CognitoActorKind;
|
|
33
33
|
/** Credential vend refused (disabled / unsupported / 403). */
|
|
34
34
|
presenceRefusal?: DaemonStateFile["presenceRefusal"];
|
|
35
|
+
/** Companies the current presence credential did not include. */
|
|
36
|
+
presenceDroppedCompanies: NonNullable<DaemonStateFile["presenceDroppedCompanies"]>;
|
|
35
37
|
/** Pending events awaiting a retry drain (POST /v1/mesh/events). */
|
|
36
38
|
emitRetryDepth: number;
|
|
37
39
|
/** Last successful direct-emit POST time. */
|
|
@@ -105,6 +105,10 @@ export function collectDaemonDoctor(deps = {}) {
|
|
|
105
105
|
`(spool=${unattributedEvents.spool} held=${unattributedEvents.held} dead-letter=${unattributedEvents.deadLetter})`);
|
|
106
106
|
}
|
|
107
107
|
const presenceRefusal = state?.presenceRefusal ?? null;
|
|
108
|
+
const presenceDroppedCompanies = state?.presenceDroppedCompanies ?? [];
|
|
109
|
+
if (presenceDroppedCompanies.length > 0) {
|
|
110
|
+
unhealthyReasons.push(`presence unavailable for ${presenceDroppedCompanies.length} companies`);
|
|
111
|
+
}
|
|
108
112
|
if (presenceRefusal) {
|
|
109
113
|
unhealthyReasons.push(`presence credential refused (${presenceRefusal.code}); next retry ${presenceRefusal.nextRetryAt}`);
|
|
110
114
|
}
|
|
@@ -132,6 +136,7 @@ export function collectDaemonDoctor(deps = {}) {
|
|
|
132
136
|
tokenSource: auth.tokenSource,
|
|
133
137
|
actorKind: auth.actorKind,
|
|
134
138
|
presenceRefusal,
|
|
139
|
+
presenceDroppedCompanies,
|
|
135
140
|
emitMode,
|
|
136
141
|
subscribed,
|
|
137
142
|
emitRetryDepth: emitRetry,
|
|
@@ -174,6 +179,13 @@ export function formatDaemonDoctor(report) {
|
|
|
174
179
|
if (report.presenceRefusal) {
|
|
175
180
|
lines.push(`presence refusal: ${report.presenceRefusal.code} (HTTP ${report.presenceRefusal.status}); next retry at ${report.presenceRefusal.nextRetryAt}`);
|
|
176
181
|
}
|
|
182
|
+
if (report.presenceDroppedCompanies.length > 0) {
|
|
183
|
+
lines.push(`presence not published for ${report.presenceDroppedCompanies.length} companies: ` +
|
|
184
|
+
report.presenceDroppedCompanies
|
|
185
|
+
.map((company) => `${company.companyUid} (${company.reason})`)
|
|
186
|
+
.join(", ") +
|
|
187
|
+
`; presence credential holds ${report.companiesOnline.length} companies; these were not included`);
|
|
188
|
+
}
|
|
177
189
|
if (report.unhealthy) {
|
|
178
190
|
lines.push(`health: UNHEALTHY`);
|
|
179
191
|
for (const reason of report.unhealthyReasons) {
|
|
@@ -162,7 +162,7 @@ export async function runMeshDaemon(deps = {}) {
|
|
|
162
162
|
});
|
|
163
163
|
let presence = null;
|
|
164
164
|
if (deps.enablePresence !== false) {
|
|
165
|
-
const
|
|
165
|
+
const fetchCredentialsRaw = deps.fetchCredentials ??
|
|
166
166
|
(async () => {
|
|
167
167
|
const t = await getToken();
|
|
168
168
|
return createContract3Fetcher({
|
|
@@ -171,6 +171,11 @@ export async function runMeshDaemon(deps = {}) {
|
|
|
171
171
|
env,
|
|
172
172
|
})();
|
|
173
173
|
});
|
|
174
|
+
const fetchCredentials = async () => {
|
|
175
|
+
const bundle = await fetchCredentialsRaw();
|
|
176
|
+
patchDaemonState(dir, { presenceDroppedCompanies: bundle.droppedCompanies }, now);
|
|
177
|
+
return bundle;
|
|
178
|
+
};
|
|
174
179
|
const presenceOpts = {
|
|
175
180
|
fetchCredentials,
|
|
176
181
|
mqttConnect: deps.mqttConnect,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { MqttConnectionState } from "./presence.js";
|
|
5
5
|
import type { FlushSummary } from "../flush.js";
|
|
6
|
+
import type { DroppedPresenceCompany } from "./credentials.js";
|
|
6
7
|
export interface PresenceRefusalState {
|
|
7
8
|
code: string;
|
|
8
9
|
status: number;
|
|
@@ -14,6 +15,8 @@ export interface DaemonStateFile {
|
|
|
14
15
|
startedAt: string;
|
|
15
16
|
mqttState: MqttConnectionState;
|
|
16
17
|
companiesOnline: string[];
|
|
18
|
+
/** Companies omitted from the current presence credential bundle. */
|
|
19
|
+
presenceDroppedCompanies?: DroppedPresenceCompany[];
|
|
17
20
|
actorUid?: string;
|
|
18
21
|
lastFlushAt?: string;
|
|
19
22
|
lastFlushResult?: FlushSummary & {
|
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
export declare const TELEMETRY_TIMEOUT_MS = 1200;
|
|
2
|
+
/**
|
|
3
|
+
* Exact stderr line printed once on the first signed-in TTY run that is not
|
|
4
|
+
* `--json`. Wording matches the event body: CLI version + timestamp only.
|
|
5
|
+
*/
|
|
6
|
+
export declare const TELEMETRY_DISCLOSURE_LINE = "HQ sends a usage event when you run a signed-in command (CLI version and a timestamp, never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.";
|
|
7
|
+
/**
|
|
8
|
+
* `HQ_TELEMETRY=0` (also `false` and `off`, case-insensitive) disables every
|
|
9
|
+
* CLI POST to `/v1/telemetry/events`. `HQ_TELEMETRY_TIMEOUT_MS` is unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export declare function isCliTelemetryDisabled(env?: NodeJS.ProcessEnv): boolean;
|
|
12
|
+
export declare function telemetryDisclosureMarkerPath(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Print the one-line disclosure at most once per machine. Returns whether
|
|
15
|
+
* the notice has been shown (this run or a previous one). `--json` and
|
|
16
|
+
* non-TTY stdout skip the print and do not record the marker.
|
|
17
|
+
*/
|
|
18
|
+
export declare function maybeDiscloseCliTelemetry(stderr?: {
|
|
19
|
+
write: (chunk: string) => unknown;
|
|
20
|
+
}, stdout?: {
|
|
21
|
+
isTTY?: boolean;
|
|
22
|
+
}, argv?: readonly string[]): boolean;
|
|
2
23
|
/**
|
|
3
24
|
* Emit the authenticated CLI session signal using an already-cached token.
|
|
4
25
|
* This deliberately never refreshes a session or opens a browser.
|
|
@@ -1,8 +1,80 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
1
4
|
import { CLI_VERSION } from "../cli-version.js";
|
|
2
5
|
import { isExpiring, isMachineIdentity, loadCachedTokens, } from "./cognito-session.js";
|
|
3
6
|
import { vaultApiFetch } from "./vault-api.js";
|
|
4
7
|
export const TELEMETRY_TIMEOUT_MS = 1_200;
|
|
8
|
+
/**
|
|
9
|
+
* Exact stderr line printed once on the first signed-in TTY run that is not
|
|
10
|
+
* `--json`. Wording matches the event body: CLI version + timestamp only.
|
|
11
|
+
*/
|
|
12
|
+
export const TELEMETRY_DISCLOSURE_LINE = "HQ sends a usage event when you run a signed-in command (CLI version and a timestamp, never command names, file contents, or secrets) to improve the CLI. Set HQ_TELEMETRY=0 to turn this off.";
|
|
13
|
+
const TELEMETRY_DISABLED_VALUES = new Set(["0", "false", "off"]);
|
|
14
|
+
const DISCLOSURE_MARKER = "cli-telemetry-disclosed";
|
|
5
15
|
let cliSessionStartedPromise;
|
|
16
|
+
/**
|
|
17
|
+
* `HQ_TELEMETRY=0` (also `false` and `off`, case-insensitive) disables every
|
|
18
|
+
* CLI POST to `/v1/telemetry/events`. `HQ_TELEMETRY_TIMEOUT_MS` is unchanged.
|
|
19
|
+
*/
|
|
20
|
+
export function isCliTelemetryDisabled(env = process.env) {
|
|
21
|
+
const raw = env.HQ_TELEMETRY?.trim().toLowerCase();
|
|
22
|
+
if (!raw)
|
|
23
|
+
return false;
|
|
24
|
+
return TELEMETRY_DISABLED_VALUES.has(raw);
|
|
25
|
+
}
|
|
26
|
+
function hqConfigDir() {
|
|
27
|
+
const home = process.env.HOME?.trim() || os.homedir();
|
|
28
|
+
return path.join(home, ".hq");
|
|
29
|
+
}
|
|
30
|
+
export function telemetryDisclosureMarkerPath() {
|
|
31
|
+
return path.join(hqConfigDir(), DISCLOSURE_MARKER);
|
|
32
|
+
}
|
|
33
|
+
function wantsJsonOutput(argv = process.argv) {
|
|
34
|
+
return argv.includes("--json");
|
|
35
|
+
}
|
|
36
|
+
function canPrintDisclosure(stdout = process.stdout, argv = process.argv) {
|
|
37
|
+
return Boolean(stdout.isTTY) && !wantsJsonOutput(argv);
|
|
38
|
+
}
|
|
39
|
+
function hasShownDisclosure() {
|
|
40
|
+
try {
|
|
41
|
+
return fs.existsSync(telemetryDisclosureMarkerPath());
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function recordDisclosureShown() {
|
|
48
|
+
try {
|
|
49
|
+
const dir = hqConfigDir();
|
|
50
|
+
// Do not create ~/.hq. Signed-in runs already have it (cached tokens).
|
|
51
|
+
if (!fs.existsSync(dir))
|
|
52
|
+
return;
|
|
53
|
+
fs.writeFileSync(telemetryDisclosureMarkerPath(), `${new Date().toISOString()}\n`);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Marker write is best effort; a repeat print is better than crashing.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Print the one-line disclosure at most once per machine. Returns whether
|
|
61
|
+
* the notice has been shown (this run or a previous one). `--json` and
|
|
62
|
+
* non-TTY stdout skip the print and do not record the marker.
|
|
63
|
+
*/
|
|
64
|
+
export function maybeDiscloseCliTelemetry(stderr = process.stderr, stdout = process.stdout, argv = process.argv) {
|
|
65
|
+
if (hasShownDisclosure())
|
|
66
|
+
return true;
|
|
67
|
+
if (!canPrintDisclosure(stdout, argv))
|
|
68
|
+
return false;
|
|
69
|
+
try {
|
|
70
|
+
stderr.write(`${TELEMETRY_DISCLOSURE_LINE}\n`);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
recordDisclosureShown();
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
6
78
|
/**
|
|
7
79
|
* Emit the authenticated CLI session signal using an already-cached token.
|
|
8
80
|
* This deliberately never refreshes a session or opens a browser.
|
|
@@ -13,12 +85,16 @@ export function emitCliSessionStarted(timeoutMs = TELEMETRY_TIMEOUT_MS) {
|
|
|
13
85
|
}
|
|
14
86
|
async function emitCachedCliSessionStarted(timeoutMs) {
|
|
15
87
|
try {
|
|
88
|
+
if (isCliTelemetryDisabled())
|
|
89
|
+
return;
|
|
16
90
|
const cached = loadCachedTokens();
|
|
17
91
|
if (!cached || isExpiring(cached, 120))
|
|
18
92
|
return;
|
|
19
93
|
const token = isMachineIdentity() ? cached.idToken : cached.accessToken;
|
|
20
94
|
if (!token)
|
|
21
95
|
return;
|
|
96
|
+
const disclosed = maybeDiscloseCliTelemetry();
|
|
97
|
+
const consentBasis = disclosed ? "disclosed" : "no-consent";
|
|
22
98
|
const controller = new AbortController();
|
|
23
99
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
24
100
|
try {
|
|
@@ -33,7 +109,7 @@ async function emitCachedCliSessionStarted(timeoutMs) {
|
|
|
33
109
|
app: "hq-cli",
|
|
34
110
|
source: "cli",
|
|
35
111
|
occurredAt: new Date().toISOString(),
|
|
36
|
-
consentBasis
|
|
112
|
+
consentBasis,
|
|
37
113
|
schemaVersion: 1,
|
|
38
114
|
properties: { version: CLI_VERSION },
|
|
39
115
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.123.0",
|
|
4
4
|
"description": "HQ by Indigo management CLI \u2014 modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"assets/scaffold",
|
|
14
14
|
"assets/bot-workers",
|
|
15
15
|
"assets/mesh-daemon",
|
|
16
|
-
"CHANGELOG.md"
|
|
16
|
+
"CHANGELOG.md",
|
|
17
|
+
"README.md"
|
|
17
18
|
],
|
|
18
19
|
"scripts": {
|
|
19
20
|
"build": "node scripts/generate-dsn.mjs && tsc && node scripts/generate-command-catalog.mjs --check && node scripts/copy-schemas.mjs && node scripts/chmod-bins.mjs",
|