@indigoai-us/hq-cloud 6.14.23 → 6.14.25
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/dist/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +11 -0
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-planning.d.ts +11 -0
- package/dist/bin/sync-runner-planning.d.ts.map +1 -1
- package/dist/bin/sync-runner-planning.js +37 -2
- package/dist/bin/sync-runner-planning.js.map +1 -1
- package/dist/bin/sync-runner-planning.test.js +1 -0
- package/dist/bin/sync-runner-planning.test.js.map +1 -1
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +17 -1
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +6 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +14 -2
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +155 -1
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/doctor.d.ts +119 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +485 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/doctor.test.d.ts +13 -0
- package/dist/cli/doctor.test.d.ts.map +1 -0
- package/dist/cli/doctor.test.js +485 -0
- package/dist/cli/doctor.test.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/share.js +6 -1
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/sync.d.ts +29 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +140 -2
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +223 -1
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/cognito-auth.test.js +7 -0
- package/dist/cognito-auth.test.js.map +1 -1
- package/dist/company-resolver.d.ts +31 -1
- package/dist/company-resolver.d.ts.map +1 -1
- package/dist/company-resolver.js +329 -21
- package/dist/company-resolver.js.map +1 -1
- package/dist/company-resolver.test.js +371 -1
- package/dist/company-resolver.test.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/conflict-file.d.ts.map +1 -1
- package/dist/lib/conflict-file.js +5 -1
- package/dist/lib/conflict-file.js.map +1 -1
- package/dist/lib/conflict-index.d.ts.map +1 -1
- package/dist/lib/conflict-index.js +7 -2
- package/dist/lib/conflict-index.js.map +1 -1
- package/dist/lib/machine-id.test.js +14 -0
- package/dist/lib/machine-id.test.js.map +1 -1
- package/dist/local-path-codec.d.ts +18 -0
- package/dist/local-path-codec.d.ts.map +1 -1
- package/dist/local-path-codec.js +63 -0
- package/dist/local-path-codec.js.map +1 -1
- package/dist/local-path-codec.test.d.ts +14 -0
- package/dist/local-path-codec.test.d.ts.map +1 -0
- package/dist/local-path-codec.test.js +102 -0
- package/dist/local-path-codec.test.js.map +1 -0
- package/dist/machine-auth.test.js +7 -0
- package/dist/machine-auth.test.js.map +1 -1
- package/dist/s3.d.ts +5 -0
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +129 -3
- package/dist/s3.js.map +1 -1
- package/dist/s3.symlink-materialize.test.d.ts +18 -0
- package/dist/s3.symlink-materialize.test.d.ts.map +1 -0
- package/dist/s3.symlink-materialize.test.js +394 -0
- package/dist/s3.symlink-materialize.test.js.map +1 -0
- package/dist/s3.test.js +19 -0
- package/dist/s3.test.js.map +1 -1
- package/dist/skill-telemetry.d.ts.map +1 -1
- package/dist/skill-telemetry.js +24 -3
- package/dist/skill-telemetry.js.map +1 -1
- package/dist/skill-telemetry.test.js +17 -0
- package/dist/skill-telemetry.test.js.map +1 -1
- package/dist/telemetry.d.ts +10 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +95 -4
- package/dist/telemetry.js.map +1 -1
- package/dist/telemetry.test.js +177 -0
- package/dist/telemetry.test.js.map +1 -1
- package/dist/vault-client.d.ts +42 -0
- package/dist/vault-client.d.ts.map +1 -1
- package/dist/vault-client.js +40 -0
- package/dist/vault-client.js.map +1 -1
- package/dist/watcher.d.ts.map +1 -1
- package/dist/watcher.js +10 -1
- package/dist/watcher.js.map +1 -1
- package/dist/watcher.test.js +28 -0
- package/dist/watcher.test.js.map +1 -1
- package/package.json +1 -1
- package/src/bin/sync-runner-company.ts +10 -0
- package/src/bin/sync-runner-planning.test.ts +1 -0
- package/src/bin/sync-runner-planning.ts +46 -3
- package/src/bin/sync-runner-watch-loop.ts +19 -1
- package/src/bin/sync-runner.test.ts +174 -0
- package/src/bin/sync-runner.ts +17 -1
- package/src/cli/doctor.test.ts +581 -0
- package/src/cli/doctor.ts +640 -0
- package/src/cli/index.ts +14 -0
- package/src/cli/share.ts +6 -1
- package/src/cli/sync.test.ts +281 -1
- package/src/cli/sync.ts +184 -2
- package/src/cognito-auth.test.ts +5 -0
- package/src/company-resolver.test.ts +432 -1
- package/src/company-resolver.ts +362 -23
- package/src/index.ts +10 -0
- package/src/lib/conflict-file.ts +6 -1
- package/src/lib/conflict-index.ts +7 -2
- package/src/lib/machine-id.test.ts +10 -0
- package/src/local-path-codec.test.ts +138 -0
- package/src/local-path-codec.ts +66 -0
- package/src/machine-auth.test.ts +5 -0
- package/src/s3.symlink-materialize.test.ts +492 -0
- package/src/s3.test.ts +24 -0
- package/src/s3.ts +148 -4
- package/src/skill-telemetry.test.ts +19 -0
- package/src/skill-telemetry.ts +32 -3
- package/src/telemetry.test.ts +241 -0
- package/src/telemetry.ts +124 -6
- package/src/vault-client.ts +68 -0
- package/src/watcher.test.ts +33 -0
- package/src/watcher.ts +10 -1
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
runRunnerWithLoop,
|
|
17
17
|
resolveDeletePolicy,
|
|
18
18
|
resolveSkipPersonal,
|
|
19
|
+
resolveSkipCompanies,
|
|
19
20
|
selectObjectIOFactory,
|
|
20
21
|
routeChangeToTarget,
|
|
21
22
|
buildTargetedPushArgv,
|
|
@@ -1091,6 +1092,114 @@ describe("fanout-plan", () => {
|
|
|
1091
1092
|
expect(plan.companies).toEqual([{ uid: "cmp_ghost", slug: "cmp_ghost" }]);
|
|
1092
1093
|
});
|
|
1093
1094
|
|
|
1095
|
+
it("honors HQ_SYNC_SKIP_COMPANIES slug denylist in the fanout plan", async () => {
|
|
1096
|
+
const prev = process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1097
|
+
process.env.HQ_SYNC_SKIP_COMPANIES = "acme";
|
|
1098
|
+
try {
|
|
1099
|
+
const deps = makeDeps({
|
|
1100
|
+
createVaultClient: () =>
|
|
1101
|
+
makeVaultStub({
|
|
1102
|
+
memberships: [{ companyUid: "cmp_a" }, { companyUid: "cmp_b" }],
|
|
1103
|
+
entityGet: (uid: string) =>
|
|
1104
|
+
Promise.resolve({
|
|
1105
|
+
uid,
|
|
1106
|
+
slug: uid === "cmp_a" ? "acme" : "beta",
|
|
1107
|
+
} as unknown as EntityInfo),
|
|
1108
|
+
}),
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
const code = await runRunner(["--companies"], deps);
|
|
1112
|
+
expect(code).toBe(0);
|
|
1113
|
+
const plan = deps.stdout
|
|
1114
|
+
.events()
|
|
1115
|
+
.find((e) => e.type === "fanout-plan") as Extract<
|
|
1116
|
+
RunnerEvent,
|
|
1117
|
+
{ type: "fanout-plan" }
|
|
1118
|
+
>;
|
|
1119
|
+
expect(plan.companies.map((c) => c.slug)).toEqual(["beta"]);
|
|
1120
|
+
} finally {
|
|
1121
|
+
if (prev === undefined) delete process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1122
|
+
else process.env.HQ_SYNC_SKIP_COMPANIES = prev;
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
it("fail-closes unresolved entities when a skip denylist is active", async () => {
|
|
1127
|
+
const prev = process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1128
|
+
process.env.HQ_SYNC_SKIP_COMPANIES = "paused-co";
|
|
1129
|
+
try {
|
|
1130
|
+
const deps = makeDeps({
|
|
1131
|
+
createVaultClient: () =>
|
|
1132
|
+
makeVaultStub({
|
|
1133
|
+
memberships: [{ companyUid: "cmp_ambiguous" }],
|
|
1134
|
+
entityGet: () => Promise.reject(new Error("transient discovery blip")),
|
|
1135
|
+
}),
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
const code = await runRunner(["--companies"], deps);
|
|
1139
|
+
// Omit ambiguous memberships from the plan, but retry (do not exit 0) so
|
|
1140
|
+
// watch scoped drain cannot publish without a successful upload pass.
|
|
1141
|
+
expect(code).toBe(TRANSIENT_NETWORK_EXIT);
|
|
1142
|
+
const plan = deps.stdout
|
|
1143
|
+
.events()
|
|
1144
|
+
.find((e) => e.type === "fanout-plan") as Extract<
|
|
1145
|
+
RunnerEvent,
|
|
1146
|
+
{ type: "fanout-plan" }
|
|
1147
|
+
>;
|
|
1148
|
+
// Without fail-closed, slug would stay as UID and bypass the slug denylist.
|
|
1149
|
+
expect(plan.companies).toEqual([]);
|
|
1150
|
+
} finally {
|
|
1151
|
+
if (prev === undefined) delete process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1152
|
+
else process.env.HQ_SYNC_SKIP_COMPANIES = prev;
|
|
1153
|
+
}
|
|
1154
|
+
});
|
|
1155
|
+
|
|
1156
|
+
it("still syncs resolved companies when denylist ambiguity forces a transient retry", async () => {
|
|
1157
|
+
const prev = process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1158
|
+
process.env.HQ_SYNC_SKIP_COMPANIES = "paused-co";
|
|
1159
|
+
try {
|
|
1160
|
+
const sync = vi.fn().mockResolvedValue(defaultSyncResult());
|
|
1161
|
+
const deps = makeDeps({
|
|
1162
|
+
createVaultClient: () =>
|
|
1163
|
+
makeVaultStub({
|
|
1164
|
+
memberships: [
|
|
1165
|
+
{ companyUid: "cmp_live" },
|
|
1166
|
+
{ companyUid: "cmp_ambiguous" },
|
|
1167
|
+
],
|
|
1168
|
+
entityGet: (uid: string) => {
|
|
1169
|
+
if (uid === "cmp_ambiguous") {
|
|
1170
|
+
return Promise.reject(new Error("transient discovery blip"));
|
|
1171
|
+
}
|
|
1172
|
+
return Promise.resolve({
|
|
1173
|
+
uid,
|
|
1174
|
+
slug: "live",
|
|
1175
|
+
type: "company",
|
|
1176
|
+
status: "active",
|
|
1177
|
+
createdAt: "2026-01-01T00:00:00Z",
|
|
1178
|
+
});
|
|
1179
|
+
},
|
|
1180
|
+
}),
|
|
1181
|
+
sync,
|
|
1182
|
+
});
|
|
1183
|
+
|
|
1184
|
+
const code = await runRunner(["--companies"], deps);
|
|
1185
|
+
expect(code).toBe(TRANSIENT_NETWORK_EXIT);
|
|
1186
|
+
expect(sync).toHaveBeenCalledTimes(1);
|
|
1187
|
+
expect(sync).toHaveBeenCalledWith(
|
|
1188
|
+
expect.objectContaining({ company: "cmp_live" }),
|
|
1189
|
+
);
|
|
1190
|
+
const plan = deps.stdout
|
|
1191
|
+
.events()
|
|
1192
|
+
.find((e) => e.type === "fanout-plan") as Extract<
|
|
1193
|
+
RunnerEvent,
|
|
1194
|
+
{ type: "fanout-plan" }
|
|
1195
|
+
>;
|
|
1196
|
+
expect(plan.companies.map((c) => c.slug)).toEqual(["live"]);
|
|
1197
|
+
} finally {
|
|
1198
|
+
if (prev === undefined) delete process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
1199
|
+
else process.env.HQ_SYNC_SKIP_COMPANIES = prev;
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
|
|
1094
1203
|
it("skips a ghost membership whose entity is gone so it cannot force exit 2", async () => {
|
|
1095
1204
|
const sync = vi.fn().mockResolvedValue(defaultSyncResult());
|
|
1096
1205
|
const deps = makeDeps({
|
|
@@ -4789,6 +4898,21 @@ describe("resolveDeletePolicy", () => {
|
|
|
4789
4898
|
// child-process default. Both surfaces tested so a regression on either
|
|
4790
4899
|
// short-circuit path surfaces here.
|
|
4791
4900
|
|
|
4901
|
+
describe("resolveSkipCompanies", () => {
|
|
4902
|
+
it("parses comma-separated slugs and drops personal", () => {
|
|
4903
|
+
expect([...resolveSkipCompanies("acme, zeta ,personal,")].sort()).toEqual([
|
|
4904
|
+
"acme",
|
|
4905
|
+
"zeta",
|
|
4906
|
+
]);
|
|
4907
|
+
});
|
|
4908
|
+
|
|
4909
|
+
it("returns empty set for missing / blank", () => {
|
|
4910
|
+
expect(resolveSkipCompanies(undefined).size).toBe(0);
|
|
4911
|
+
expect(resolveSkipCompanies("").size).toBe(0);
|
|
4912
|
+
expect(resolveSkipCompanies(" ").size).toBe(0);
|
|
4913
|
+
});
|
|
4914
|
+
});
|
|
4915
|
+
|
|
4792
4916
|
describe("resolveSkipPersonal", () => {
|
|
4793
4917
|
let originalEnv: string | undefined;
|
|
4794
4918
|
|
|
@@ -5852,6 +5976,56 @@ describe("runRunnerWithLoop — Phase 3 event-sync wiring (US-017/018/019)", ()
|
|
|
5852
5976
|
await loop;
|
|
5853
5977
|
});
|
|
5854
5978
|
|
|
5979
|
+
it("publishes NOTHING for HQ_SYNC_SKIP_COMPANIES paused workspace edits", async () => {
|
|
5980
|
+
const prev = process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
5981
|
+
process.env.HQ_SYNC_SKIP_COMPANIES = "indigo";
|
|
5982
|
+
try {
|
|
5983
|
+
const publishBatch = vi.fn();
|
|
5984
|
+
const startEventSync = vi.fn().mockResolvedValue({
|
|
5985
|
+
publishBatch,
|
|
5986
|
+
receiver: {
|
|
5987
|
+
start: async () => {},
|
|
5988
|
+
dispose: async () => {},
|
|
5989
|
+
connected: true,
|
|
5990
|
+
},
|
|
5991
|
+
ownDeviceId: "dev-test",
|
|
5992
|
+
dispose: vi.fn().mockResolvedValue(undefined),
|
|
5993
|
+
});
|
|
5994
|
+
const runPass = vi.fn().mockResolvedValue(0);
|
|
5995
|
+
const { loop, watcher, clock, tick, shutdown } = runLoop({
|
|
5996
|
+
claims: ENROLLED_CLAIMS,
|
|
5997
|
+
startEventSync,
|
|
5998
|
+
runPass,
|
|
5999
|
+
});
|
|
6000
|
+
await microtasks();
|
|
6001
|
+
runPass.mockClear();
|
|
6002
|
+
|
|
6003
|
+
watcher.emit("companies/indigo/a.md", {
|
|
6004
|
+
paths: new Map([
|
|
6005
|
+
["/tmp/hq/companies/indigo/a.md", "companies/indigo/a.md"],
|
|
6006
|
+
]),
|
|
6007
|
+
});
|
|
6008
|
+
clock.advance(0);
|
|
6009
|
+
await microtasks();
|
|
6010
|
+
tick();
|
|
6011
|
+
await microtasks();
|
|
6012
|
+
|
|
6013
|
+
// Paused: no scoped push, no event-sync publish (path/hash must not leak).
|
|
6014
|
+
expect(
|
|
6015
|
+
runPass.mock.calls.filter((call) =>
|
|
6016
|
+
(call[0] as string[]).includes("--company"),
|
|
6017
|
+
),
|
|
6018
|
+
).toEqual([]);
|
|
6019
|
+
expect(publishBatch).not.toHaveBeenCalled();
|
|
6020
|
+
|
|
6021
|
+
shutdown();
|
|
6022
|
+
await loop;
|
|
6023
|
+
} finally {
|
|
6024
|
+
if (prev === undefined) delete process.env.HQ_SYNC_SKIP_COMPANIES;
|
|
6025
|
+
else process.env.HQ_SYNC_SKIP_COMPANIES = prev;
|
|
6026
|
+
}
|
|
6027
|
+
});
|
|
6028
|
+
|
|
5855
6029
|
it("does not publish an exit-zero deletion that the scoped push refused", async () => {
|
|
5856
6030
|
const publishBatch = vi.fn();
|
|
5857
6031
|
const startEventSync = vi.fn().mockResolvedValue({
|
package/src/bin/sync-runner.ts
CHANGED
|
@@ -144,9 +144,12 @@ import {
|
|
|
144
144
|
buildFanoutPlan,
|
|
145
145
|
emitFanoutPlan,
|
|
146
146
|
resolveMembershipsForRun,
|
|
147
|
+
resolveSkipCompanies,
|
|
147
148
|
type RunnerTarget,
|
|
148
149
|
type SetupNeededReason,
|
|
149
150
|
} from "./sync-runner-planning.js";
|
|
151
|
+
|
|
152
|
+
export { resolveSkipCompanies } from "./sync-runner-planning.js";
|
|
150
153
|
import {
|
|
151
154
|
seedActiveCompany,
|
|
152
155
|
type SeedActiveCompanyOptions,
|
|
@@ -394,6 +397,10 @@ export type RunnerEvent =
|
|
|
394
397
|
Extract<SyncProgressEvent, { type: "skip-invalid-scoped-key" }>,
|
|
395
398
|
"type"
|
|
396
399
|
>)
|
|
400
|
+
| ({ type: "skip-junk-key-spelling"; company: string } & Omit<
|
|
401
|
+
Extract<SyncProgressEvent, { type: "skip-junk-key-spelling" }>,
|
|
402
|
+
"type"
|
|
403
|
+
>)
|
|
397
404
|
| ({
|
|
398
405
|
type: "complete";
|
|
399
406
|
company: string;
|
|
@@ -1441,6 +1448,7 @@ export async function runRunner(
|
|
|
1441
1448
|
client,
|
|
1442
1449
|
claims,
|
|
1443
1450
|
resolveSkipPersonal,
|
|
1451
|
+
skipCompanies: resolveSkipCompanies(),
|
|
1444
1452
|
});
|
|
1445
1453
|
} catch (err) {
|
|
1446
1454
|
if (err instanceof VaultAuthError) {
|
|
@@ -1461,6 +1469,7 @@ export async function runRunner(
|
|
|
1461
1469
|
return 0;
|
|
1462
1470
|
}
|
|
1463
1471
|
const plan = targetPlan.plan;
|
|
1472
|
+
const deferredUnresolved = targetPlan.deferredUnresolved;
|
|
1464
1473
|
|
|
1465
1474
|
emitFanoutPlan(emit, plan);
|
|
1466
1475
|
|
|
@@ -1882,7 +1891,14 @@ export async function runRunner(
|
|
|
1882
1891
|
// otherwise fire for normal user-policy outcomes. Consumers that want to
|
|
1883
1892
|
// flag any non-clean outcome (clean-abort + thrown-error) read `partial`
|
|
1884
1893
|
// from the JSON. The watch loop stays alive on this code (see exit-codes.ts).
|
|
1885
|
-
|
|
1894
|
+
//
|
|
1895
|
+
// deferredUnresolved: a slug denylist was active but entity discovery blipped
|
|
1896
|
+
// for one or more memberships we could not prove were paused. Fail closed on
|
|
1897
|
+
// the plan (they were omitted) and return TRANSIENT so watch scoped drain
|
|
1898
|
+
// does not publish without a successful upload pass.
|
|
1899
|
+
if (errors.length > 0) return PARTIAL_SYNC_EXIT;
|
|
1900
|
+
if (deferredUnresolved > 0) return TRANSIENT_NETWORK_EXIT;
|
|
1901
|
+
return 0;
|
|
1886
1902
|
}
|
|
1887
1903
|
|
|
1888
1904
|
// ---------------------------------------------------------------------------
|