@indigoai-us/hq-cloud 6.15.0 → 6.15.2
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-mutation.d.ts +16 -0
- package/dist/bin/sync-mutation.d.ts.map +1 -0
- package/dist/bin/sync-mutation.js +60 -0
- package/dist/bin/sync-mutation.js.map +1 -0
- package/dist/bin/sync-mutation.test.d.ts +2 -0
- package/dist/bin/sync-mutation.test.d.ts.map +1 -0
- package/dist/bin/sync-mutation.test.js +165 -0
- package/dist/bin/sync-mutation.test.js.map +1 -0
- package/dist/bin/sync-runner-company.d.ts +8 -0
- package/dist/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +16 -0
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-company.test.d.ts +2 -0
- package/dist/bin/sync-runner-company.test.d.ts.map +1 -0
- package/dist/bin/sync-runner-company.test.js +36 -0
- package/dist/bin/sync-runner-company.test.js.map +1 -0
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +98 -8
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +17 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +109 -0
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/conflict-recovery.test.d.ts +2 -0
- package/dist/cli/conflict-recovery.test.d.ts.map +1 -0
- package/dist/cli/conflict-recovery.test.js +201 -0
- package/dist/cli/conflict-recovery.test.js.map +1 -0
- package/dist/cli/conflict.d.ts +60 -0
- package/dist/cli/conflict.d.ts.map +1 -1
- package/dist/cli/conflict.js +333 -0
- package/dist/cli/conflict.js.map +1 -1
- package/dist/cli/sync.d.ts +35 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +100 -0
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +85 -1
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/skill-telemetry.d.ts +6 -0
- package/dist/skill-telemetry.d.ts.map +1 -1
- package/dist/skill-telemetry.js +14 -2
- package/dist/skill-telemetry.js.map +1 -1
- package/dist/skill-telemetry.test.js +79 -0
- package/dist/skill-telemetry.test.js.map +1 -1
- package/dist/sync/candidate-uploader.d.ts +88 -0
- package/dist/sync/candidate-uploader.d.ts.map +1 -0
- package/dist/sync/candidate-uploader.js +212 -0
- package/dist/sync/candidate-uploader.js.map +1 -0
- package/dist/sync/candidate-uploader.test.d.ts +2 -0
- package/dist/sync/candidate-uploader.test.d.ts.map +1 -0
- package/dist/sync/candidate-uploader.test.js +132 -0
- package/dist/sync/candidate-uploader.test.js.map +1 -0
- package/dist/sync/delta-client.d.ts +73 -0
- package/dist/sync/delta-client.d.ts.map +1 -0
- package/dist/sync/delta-client.js +201 -0
- package/dist/sync/delta-client.js.map +1 -0
- package/dist/sync/delta-client.test.d.ts +2 -0
- package/dist/sync/delta-client.test.d.ts.map +1 -0
- package/dist/sync/delta-client.test.js +97 -0
- package/dist/sync/delta-client.test.js.map +1 -0
- package/dist/sync/durable-apply.d.ts +76 -0
- package/dist/sync/durable-apply.d.ts.map +1 -0
- package/dist/sync/durable-apply.js +530 -0
- package/dist/sync/durable-apply.js.map +1 -0
- package/dist/sync/durable-apply.test.d.ts +2 -0
- package/dist/sync/durable-apply.test.d.ts.map +1 -0
- package/dist/sync/durable-apply.test.js +180 -0
- package/dist/sync/durable-apply.test.js.map +1 -0
- package/dist/sync/event-sync.d.ts +33 -1
- package/dist/sync/event-sync.d.ts.map +1 -1
- package/dist/sync/event-sync.js +149 -1
- package/dist/sync/event-sync.js.map +1 -1
- package/dist/sync/event-sync.test.js +142 -1
- package/dist/sync/event-sync.test.js.map +1 -1
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +1 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/multipart-uploader.d.ts +99 -0
- package/dist/sync/multipart-uploader.d.ts.map +1 -0
- package/dist/sync/multipart-uploader.js +447 -0
- package/dist/sync/multipart-uploader.js.map +1 -0
- package/dist/sync/multipart-uploader.test.d.ts +2 -0
- package/dist/sync/multipart-uploader.test.d.ts.map +1 -0
- package/dist/sync/multipart-uploader.test.js +119 -0
- package/dist/sync/multipart-uploader.test.js.map +1 -0
- package/dist/sync/mutation-client.d.ts +85 -0
- package/dist/sync/mutation-client.d.ts.map +1 -0
- package/dist/sync/mutation-client.js +245 -0
- package/dist/sync/mutation-client.js.map +1 -0
- package/dist/sync/mutation-client.test.d.ts +2 -0
- package/dist/sync/mutation-client.test.d.ts.map +1 -0
- package/dist/sync/mutation-client.test.js +51 -0
- package/dist/sync/mutation-client.test.js.map +1 -0
- package/dist/sync/push-receiver.d.ts +45 -0
- package/dist/sync/push-receiver.d.ts.map +1 -1
- package/dist/sync/push-receiver.js +101 -0
- package/dist/sync/push-receiver.js.map +1 -1
- package/dist/sync/push-receiver.test.js +54 -2
- package/dist/sync/push-receiver.test.js.map +1 -1
- package/dist/sync/scope-inventory-client.d.ts +69 -0
- package/dist/sync/scope-inventory-client.d.ts.map +1 -0
- package/dist/sync/scope-inventory-client.js +210 -0
- package/dist/sync/scope-inventory-client.js.map +1 -0
- package/dist/sync/scope-inventory-client.test.d.ts +2 -0
- package/dist/sync/scope-inventory-client.test.d.ts.map +1 -0
- package/dist/sync/scope-inventory-client.test.js +94 -0
- package/dist/sync/scope-inventory-client.test.js.map +1 -0
- package/dist/sync/snapshot-client.d.ts +98 -0
- package/dist/sync/snapshot-client.d.ts.map +1 -0
- package/dist/sync/snapshot-client.js +402 -0
- package/dist/sync/snapshot-client.js.map +1 -0
- package/dist/sync/snapshot-client.test.d.ts +2 -0
- package/dist/sync/snapshot-client.test.d.ts.map +1 -0
- package/dist/sync/snapshot-client.test.js +169 -0
- package/dist/sync/snapshot-client.test.js.map +1 -0
- package/dist/sync/uploader-finalization.d.ts +97 -0
- package/dist/sync/uploader-finalization.d.ts.map +1 -0
- package/dist/sync/uploader-finalization.js +273 -0
- package/dist/sync/uploader-finalization.js.map +1 -0
- package/dist/sync/uploader-finalization.test.d.ts +2 -0
- package/dist/sync/uploader-finalization.test.d.ts.map +1 -0
- package/dist/sync/uploader-finalization.test.js +92 -0
- package/dist/sync/uploader-finalization.test.js.map +1 -0
- package/dist/telemetry.d.ts +11 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +21 -2
- package/dist/telemetry.js.map +1 -1
- package/dist/telemetry.test.js +80 -0
- package/dist/telemetry.test.js.map +1 -1
- package/package.json +6 -1
- package/.claude/policies/hq-cloud-esm-cannot-spy-fs-builtins.md +0 -30
- package/.claude/policies/hq-cloud-strip-types-no-parameter-properties.md +0 -22
- package/.github/workflows/ci.yml +0 -84
- package/.github/workflows/publish.yml +0 -56
- package/.github/workflows/unreleased-commits-nag.yml +0 -256
- package/eslint.config.js +0 -67
- package/pnpm-workspace.yaml +0 -2
- package/scripts/presign-transport-e2e.mjs +0 -250
- package/scripts/vault-rebaseline.sh +0 -323
- package/scripts/vault-rescue.sh +0 -332
- package/src/active-company.test.ts +0 -188
- package/src/active-company.ts +0 -168
- package/src/agent-codex-instructions.test.ts +0 -332
- package/src/agent-codex-instructions.ts +0 -309
- package/src/auth.ts +0 -146
- package/src/backup-prune.test.ts +0 -98
- package/src/backup-prune.ts +0 -182
- package/src/bin/backup-prune-runner.ts +0 -33
- package/src/bin/rescue-runner.ts +0 -25
- package/src/bin/sync-runner-company.ts +0 -695
- package/src/bin/sync-runner-events.test.ts +0 -143
- package/src/bin/sync-runner-events.ts +0 -55
- package/src/bin/sync-runner-planning.test.ts +0 -311
- package/src/bin/sync-runner-planning.ts +0 -258
- package/src/bin/sync-runner-rollup.test.ts +0 -37
- package/src/bin/sync-runner-rollup.ts +0 -97
- package/src/bin/sync-runner-telemetry.ts +0 -15
- package/src/bin/sync-runner-watch-loop.ts +0 -1235
- package/src/bin/sync-runner-watch-routes.test.ts +0 -71
- package/src/bin/sync-runner-watch-routes.ts +0 -184
- package/src/bin/sync-runner.test.ts +0 -8767
- package/src/bin/sync-runner.ts +0 -2190
- package/src/cli/accept.ts +0 -124
- package/src/cli/conflict.ts +0 -119
- package/src/cli/doctor.test.ts +0 -581
- package/src/cli/doctor.ts +0 -642
- package/src/cli/index.ts +0 -49
- package/src/cli/invite.test.ts +0 -250
- package/src/cli/invite.ts +0 -214
- package/src/cli/promote.ts +0 -157
- package/src/cli/reindex-knowledge.test.ts +0 -307
- package/src/cli/reindex-knowledge.ts +0 -450
- package/src/cli/reindex.test.ts +0 -957
- package/src/cli/reindex.ts +0 -979
- package/src/cli/rescue-classify-ordering.test.ts +0 -548
- package/src/cli/rescue-clone-diagnostics.test.ts +0 -120
- package/src/cli/rescue-core.ts +0 -3011
- package/src/cli/rescue-drift-reconcile.test.ts +0 -179
- package/src/cli/rescue-drop-dir-symlink.test.ts +0 -224
- package/src/cli/rescue-exec-bit-preserve.test.ts +0 -187
- package/src/cli/rescue-hq-root-guard.test.ts +0 -232
- package/src/cli/rescue-journal-reconcile.test.ts +0 -215
- package/src/cli/rescue-mtime-preserve.test.ts +0 -203
- package/src/cli/rescue-settings-reconcile.test.ts +0 -637
- package/src/cli/rescue-snapshot.test.ts +0 -57
- package/src/cli/rescue-snapshot.ts +0 -51
- package/src/cli/rescue.reindex.test.ts +0 -63
- package/src/cli/rescue.test.ts +0 -131
- package/src/cli/rescue.ts +0 -182
- package/src/cli/share.test.ts +0 -7843
- package/src/cli/share.ts +0 -3663
- package/src/cli/sync-scope.test.ts +0 -652
- package/src/cli/sync.test.ts +0 -5207
- package/src/cli/sync.ts +0 -3470
- package/src/cli/tombstones.ts +0 -106
- package/src/cli/watch-event-push-conflict.test.ts +0 -234
- package/src/client-info.test.ts +0 -214
- package/src/client-info.ts +0 -121
- package/src/cognito-auth.test.ts +0 -712
- package/src/cognito-auth.ts +0 -1422
- package/src/company-resolver.test.ts +0 -618
- package/src/company-resolver.ts +0 -521
- package/src/context.test.ts +0 -583
- package/src/context.ts +0 -378
- package/src/daemon-worker.ts +0 -26
- package/src/daemon.ts +0 -99
- package/src/entity-resolver.test.ts +0 -315
- package/src/entity-resolver.ts +0 -180
- package/src/ignore.test.ts +0 -466
- package/src/ignore.ts +0 -469
- package/src/index.ts +0 -439
- package/src/journal.test.ts +0 -968
- package/src/journal.ts +0 -765
- package/src/lib/cloud-authoritative.test.ts +0 -45
- package/src/lib/cloud-authoritative.ts +0 -59
- package/src/lib/conflict-file.ts +0 -86
- package/src/lib/conflict-index.ts +0 -289
- package/src/lib/conflict.test.ts +0 -348
- package/src/lib/describe-error.test.ts +0 -100
- package/src/lib/describe-error.ts +0 -58
- package/src/lib/exit-codes.ts +0 -24
- package/src/lib/machine-id.test.ts +0 -231
- package/src/lib/machine-id.ts +0 -175
- package/src/lib/net-errors.test.ts +0 -65
- package/src/lib/net-errors.ts +0 -86
- package/src/lib/readlink-safe.test.ts +0 -43
- package/src/lib/readlink-safe.ts +0 -29
- package/src/local-path-codec.test.ts +0 -138
- package/src/local-path-codec.ts +0 -161
- package/src/machine-auth.test.ts +0 -1323
- package/src/manifest-reconcile.test.ts +0 -1123
- package/src/manifest-reconcile.ts +0 -518
- package/src/object-io.test.ts +0 -1221
- package/src/object-io.ts +0 -1306
- package/src/operation-lock.test.ts +0 -484
- package/src/operation-lock.ts +0 -680
- package/src/outcome-telemetry.test.ts +0 -498
- package/src/outcome-telemetry.ts +0 -639
- package/src/personal-vault-exclusions.test.ts +0 -308
- package/src/personal-vault-exclusions.ts +0 -354
- package/src/personal-vault.test.ts +0 -756
- package/src/personal-vault.ts +0 -496
- package/src/prefix-coalesce.test.ts +0 -240
- package/src/prefix-coalesce.ts +0 -273
- package/src/public-surface.test.ts +0 -117
- package/src/qmd-reindex.test.ts +0 -877
- package/src/qmd-reindex.ts +0 -842
- package/src/read-only-state-dir.test.ts +0 -188
- package/src/remote-pull.test.ts +0 -1130
- package/src/remote-pull.ts +0 -618
- package/src/s3.symlink-materialize.test.ts +0 -492
- package/src/s3.test.ts +0 -1789
- package/src/s3.ts +0 -1532
- package/src/schemas/signal-types.test.ts +0 -82
- package/src/schemas/signal-types.ts +0 -38
- package/src/schemas/source-channels.test.ts +0 -82
- package/src/schemas/source-channels.ts +0 -53
- package/src/scope-shrink.test.ts +0 -633
- package/src/scope-shrink.ts +0 -481
- package/src/signals/get.test.ts +0 -310
- package/src/signals/get.ts +0 -75
- package/src/signals/internals.ts +0 -195
- package/src/signals/list.test.ts +0 -420
- package/src/signals/list.ts +0 -79
- package/src/signals/parse.ts +0 -8
- package/src/signals/types.ts +0 -91
- package/src/skill-telemetry.test.ts +0 -1825
- package/src/skill-telemetry.ts +0 -1439
- package/src/sources/get.test.ts +0 -293
- package/src/sources/get.ts +0 -66
- package/src/sources/internals.ts +0 -198
- package/src/sources/list.test.ts +0 -402
- package/src/sources/list.ts +0 -84
- package/src/sources/parse.ts +0 -43
- package/src/sources/types.ts +0 -84
- package/src/sync/event-sync.test.ts +0 -594
- package/src/sync/event-sync.ts +0 -545
- package/src/sync/feature-flags.test.ts +0 -378
- package/src/sync/feature-flags.ts +0 -62
- package/src/sync/index.ts +0 -76
- package/src/sync/lease-client.test.ts +0 -128
- package/src/sync/lease-client.ts +0 -207
- package/src/sync/logger.test.ts +0 -242
- package/src/sync/logger.ts +0 -79
- package/src/sync/metrics.test.ts +0 -462
- package/src/sync/metrics.ts +0 -213
- package/src/sync/pull-scope.ts +0 -265
- package/src/sync/push-event.test.ts +0 -266
- package/src/sync/push-event.ts +0 -224
- package/src/sync/push-receiver.test.ts +0 -566
- package/src/sync/push-receiver.ts +0 -1048
- package/src/sync/push-transport.ts +0 -231
- package/src/sync/realtime-rollout.test.ts +0 -86
- package/src/sync/realtime-rollout.ts +0 -262
- package/src/sync/state-store.test.ts +0 -194
- package/src/sync/state-store.ts +0 -727
- package/src/sync-core.ts +0 -58
- package/src/sync-progress.test.ts +0 -94
- package/src/sync-progress.ts +0 -140
- package/src/telemetry-events.test.ts +0 -88
- package/src/telemetry-events.ts +0 -205
- package/src/telemetry.test.ts +0 -1280
- package/src/telemetry.ts +0 -1109
- package/src/types.ts +0 -314
- package/src/vault-client.test.ts +0 -1380
- package/src/vault-client.ts +0 -1694
- package/src/version.ts +0 -24
- package/src/watch-roots.test.ts +0 -278
- package/src/watch-roots.ts +0 -162
- package/src/watcher-event-gate.test.ts +0 -212
- package/src/watcher.test.ts +0 -1079
- package/src/watcher.ts +0 -1741
- package/test/e2e/sync/cross-tenant-isolation.test.ts +0 -630
- package/test/e2e/sync/skill-telemetry-oversized-transcript.test.ts +0 -124
- package/test/e2e/sync/transient-company-leg.test.ts +0 -384
- package/test/e2e/sync/windows-unreadable-link-leg.test.ts +0 -191
- package/test/e2e/watcher-real-chokidar.test.ts +0 -165
- package/test/e2e/watcher-recursive-backend.test.ts +0 -181
- package/test/e2e/watcher-scoped-coverage.test.ts +0 -381
- package/test/invite-flow.integration.test.ts +0 -244
- package/test/joiner-manifest-reconcile.integration.test.ts +0 -322
- package/test/share-sync.integration.test.ts +0 -213
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -22
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
import { VaultAuthError, VaultNotFoundError, type Membership } from "../index.js";
|
|
2
|
-
import {
|
|
3
|
-
pickCanonicalPersonEntity,
|
|
4
|
-
pickAgentSelfEntity,
|
|
5
|
-
} from "../vault-client.js";
|
|
6
|
-
import { PERSONAL_VAULT_JOURNAL_SLUG } from "../journal.js";
|
|
7
|
-
import type { RunnerEvent, VaultClientSurface } from "./sync-runner.js";
|
|
8
|
-
|
|
9
|
-
interface IdentityClaims {
|
|
10
|
-
sub?: string;
|
|
11
|
-
email?: string;
|
|
12
|
-
name?: string;
|
|
13
|
-
given_name?: string;
|
|
14
|
-
family_name?: string;
|
|
15
|
-
/**
|
|
16
|
-
* Entity-bound machine-principal claims (US-013, unblocks US-004). A
|
|
17
|
-
* headless agent or Outpost authenticates with machine credentials; its
|
|
18
|
-
* idToken carries the machine's own entity binding here. The `--personal`
|
|
19
|
-
* target resolver reads agent claims to resolve the agent AS itself: when
|
|
20
|
-
* `custom:entityType === "agent"`, the personal slot resolves to the
|
|
21
|
-
* agent's OWN entity (`custom:entityUid`, `agt_*`) instead of the
|
|
22
|
-
* person-only canonical pick.
|
|
23
|
-
*/
|
|
24
|
-
"custom:entityType"?: string;
|
|
25
|
-
"custom:entityUid"?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface RunnerTarget {
|
|
29
|
-
uid: string;
|
|
30
|
-
slug: string;
|
|
31
|
-
name?: string;
|
|
32
|
-
bucketName?: string;
|
|
33
|
-
/**
|
|
34
|
-
* Entity liveness as observed AT PLAN TIME, copied off the `entity.get`
|
|
35
|
-
* result this plan was built from. Carried so a downstream consumer can make
|
|
36
|
-
* a liveness decision without paying a second round trip.
|
|
37
|
-
*
|
|
38
|
-
* These deliberately do NOT gate SYNC. A suspended company still syncs — that
|
|
39
|
-
* is user-visible behavior and not ours to change here. They exist so the
|
|
40
|
-
* MANIFEST decision (see `manifest-reconcile.ts`) can be as strict as it was
|
|
41
|
-
* when it re-fetched the entity itself. Absent fields mean "unknown", and
|
|
42
|
-
* every consumer of them is required to fail closed.
|
|
43
|
-
*/
|
|
44
|
-
entityType?: string;
|
|
45
|
-
entityStatus?: string;
|
|
46
|
-
personalMode?: boolean;
|
|
47
|
-
journalSlug?: string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Why a run cannot proceed to a fanout.
|
|
52
|
-
*
|
|
53
|
-
* - `no-memberships` — the caller resolved fine but belongs to no company.
|
|
54
|
-
* Usually a joiner whose invite is still pending acceptance.
|
|
55
|
-
* - `no-person-entity` — the caller is signed in but has no personal entity to
|
|
56
|
-
* sync into, so even `--personal` has no target.
|
|
57
|
-
*/
|
|
58
|
-
export type SetupNeededReason = "no-memberships" | "no-person-entity";
|
|
59
|
-
|
|
60
|
-
export type MembershipResolution =
|
|
61
|
-
| { status: "setup-needed"; reason: SetupNeededReason; pendingInviteCount?: number }
|
|
62
|
-
| { status: "memberships"; memberships: Pick<Membership, "companyUid">[] };
|
|
63
|
-
|
|
64
|
-
export async function resolveMembershipsForRun(options: {
|
|
65
|
-
personal: boolean;
|
|
66
|
-
companies: boolean;
|
|
67
|
-
company?: string;
|
|
68
|
-
client: VaultClientSurface;
|
|
69
|
-
claims: IdentityClaims | null;
|
|
70
|
-
stderr: { write: (chunk: string) => boolean | void };
|
|
71
|
-
/** Returns how many invites were still pending after the claim attempt. */
|
|
72
|
-
runClaimDance: (
|
|
73
|
-
client: VaultClientSurface,
|
|
74
|
-
claims: IdentityClaims,
|
|
75
|
-
stderr: { write: (chunk: string) => boolean | void },
|
|
76
|
-
) => Promise<number>;
|
|
77
|
-
listMemberships: (
|
|
78
|
-
client: VaultClientSurface,
|
|
79
|
-
) => Promise<Pick<Membership, "companyUid">[]>;
|
|
80
|
-
}): Promise<MembershipResolution> {
|
|
81
|
-
if (options.personal) {
|
|
82
|
-
return { status: "memberships", memberships: [] };
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (options.companies) {
|
|
86
|
-
let pendingInviteCount = 0;
|
|
87
|
-
if (options.claims && options.claims["custom:entityType"] !== "outpost") {
|
|
88
|
-
pendingInviteCount = await options.runClaimDance(
|
|
89
|
-
options.client,
|
|
90
|
-
options.claims,
|
|
91
|
-
options.stderr,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const memberships = await options.listMemberships(options.client);
|
|
96
|
-
if (memberships.length === 0) {
|
|
97
|
-
// Pending invites that did NOT become memberships are the single most
|
|
98
|
-
// useful thing we can tell this user: they are not "solo", they have an
|
|
99
|
-
// invite waiting to be accepted. Reported only on the empty path, where
|
|
100
|
-
// it explains the emptiness.
|
|
101
|
-
return {
|
|
102
|
-
status: "setup-needed",
|
|
103
|
-
reason: "no-memberships",
|
|
104
|
-
...(pendingInviteCount > 0 ? { pendingInviteCount } : {}),
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
return { status: "memberships", memberships };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
status: "memberships",
|
|
112
|
-
memberships: [{ companyUid: options.company! }],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Company slugs the desktop app has paused via per-workspace Off toggles.
|
|
118
|
-
*
|
|
119
|
-
* `HQ_SYNC_SKIP_COMPANIES` is a comma-separated slug list (whitespace tolerated).
|
|
120
|
-
* Empty / missing → no filter. Used by `--companies` fanout and the watch
|
|
121
|
-
* scoped-drain so Sync Now / Auto-sync honor `workspaceSyncEnabled=false`.
|
|
122
|
-
*/
|
|
123
|
-
export function resolveSkipCompanies(
|
|
124
|
-
envValue: string | undefined = process.env.HQ_SYNC_SKIP_COMPANIES,
|
|
125
|
-
): Set<string> {
|
|
126
|
-
const raw = (envValue ?? "").trim();
|
|
127
|
-
if (!raw) return new Set();
|
|
128
|
-
return new Set(
|
|
129
|
-
raw
|
|
130
|
-
.split(",")
|
|
131
|
-
.map((slug) => slug.trim().toLowerCase())
|
|
132
|
-
.filter((slug) => slug.length > 0 && slug !== "personal"),
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export async function buildFanoutPlan(options: {
|
|
137
|
-
memberships: Pick<Membership, "companyUid">[];
|
|
138
|
-
companies: boolean;
|
|
139
|
-
personal: boolean;
|
|
140
|
-
skipPersonal: boolean;
|
|
141
|
-
client: VaultClientSurface;
|
|
142
|
-
claims: IdentityClaims | null;
|
|
143
|
-
resolveSkipPersonal: (flag: boolean) => boolean;
|
|
144
|
-
/** Optional company-slug denylist (desktop per-workspace Off toggles). */
|
|
145
|
-
skipCompanies?: Set<string>;
|
|
146
|
-
}): Promise<
|
|
147
|
-
| { status: "setup-needed"; reason: SetupNeededReason }
|
|
148
|
-
| { status: "plan"; plan: RunnerTarget[]; deferredUnresolved: number }
|
|
149
|
-
> {
|
|
150
|
-
const skipCompanies = options.skipCompanies ?? new Set<string>();
|
|
151
|
-
const plan: RunnerTarget[] = [];
|
|
152
|
-
let deferredUnresolved = 0;
|
|
153
|
-
for (const m of options.memberships) {
|
|
154
|
-
let slug = m.companyUid;
|
|
155
|
-
let name: string | undefined;
|
|
156
|
-
// Carried onto the target so downstream consumers (manifest reconciliation)
|
|
157
|
-
// can use the bucket, and judge liveness, without re-fetching the entity we
|
|
158
|
-
// already resolved here. On the degraded catch path below these stay
|
|
159
|
-
// undefined, which every consumer must read as "unknown" and reject.
|
|
160
|
-
let bucketName: string | undefined;
|
|
161
|
-
let entityType: string | undefined;
|
|
162
|
-
let entityStatus: string | undefined;
|
|
163
|
-
let entityResolved = false;
|
|
164
|
-
try {
|
|
165
|
-
const info = await options.client.entity.get(m.companyUid);
|
|
166
|
-
if (!info) continue;
|
|
167
|
-
slug = info.slug || m.companyUid;
|
|
168
|
-
name = info.name;
|
|
169
|
-
bucketName = info.bucketName;
|
|
170
|
-
// Recorded, never acted on here: a suspended or unexpectedly-typed
|
|
171
|
-
// company still gets a sync leg exactly as before. Only the manifest
|
|
172
|
-
// decision consumes these.
|
|
173
|
-
entityType = info.type;
|
|
174
|
-
entityStatus = info.status;
|
|
175
|
-
entityResolved = true;
|
|
176
|
-
} catch (err) {
|
|
177
|
-
if (err instanceof VaultAuthError) throw err;
|
|
178
|
-
// A stale membership can outlive its company entity. Do not schedule a
|
|
179
|
-
// target that the sync leg can only fail to resolve later. Other lookup
|
|
180
|
-
// failures stay best-effort so a transient discovery blip does not
|
|
181
|
-
// silently drop an otherwise valid company from this run — unless a
|
|
182
|
-
// slug denylist is active (see fail-closed / deferred check below).
|
|
183
|
-
if (
|
|
184
|
-
err instanceof VaultNotFoundError ||
|
|
185
|
-
(typeof err === "object" &&
|
|
186
|
-
err !== null &&
|
|
187
|
-
"statusCode" in err &&
|
|
188
|
-
err.statusCode === 404)
|
|
189
|
-
) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
// Best-effort — keep UID as the display identifier.
|
|
193
|
-
}
|
|
194
|
-
// Desktop Off toggles ship slug denylists. Proven matches (slug or UID)
|
|
195
|
-
// skip cleanly. When a denylist is active but entity lookup failed and the
|
|
196
|
-
// UID itself is not listed, we cannot tell paused-by-slug from an enabled
|
|
197
|
-
// company that blipped — omit from the plan (do not sync ambiguous) and
|
|
198
|
-
// ask the runner to retry (non-zero) so watch scoped drain cannot publish
|
|
199
|
-
// without a successful upload pass.
|
|
200
|
-
if (
|
|
201
|
-
skipCompanies.has(slug.toLowerCase()) ||
|
|
202
|
-
skipCompanies.has(m.companyUid.toLowerCase())
|
|
203
|
-
) {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (skipCompanies.size > 0 && !entityResolved) {
|
|
207
|
-
deferredUnresolved += 1;
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
plan.push({
|
|
211
|
-
uid: m.companyUid,
|
|
212
|
-
slug,
|
|
213
|
-
...(name ? { name } : {}),
|
|
214
|
-
...(bucketName ? { bucketName } : {}),
|
|
215
|
-
...(entityType ? { entityType } : {}),
|
|
216
|
-
...(entityStatus ? { entityStatus } : {}),
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (
|
|
221
|
-
(options.companies || options.personal) &&
|
|
222
|
-
!options.resolveSkipPersonal(options.skipPersonal)
|
|
223
|
-
) {
|
|
224
|
-
// Self-entity listing. `/entity/by-type/:type` is type-filtered: querying
|
|
225
|
-
// `person` for an agent JWT returns no agent entity in production. Query
|
|
226
|
-
// the caller-scoped agent collection for a machine identity instead, then
|
|
227
|
-
// require the JWT-bound UID below. A machine caller must never fall back
|
|
228
|
-
// to a person entity, which could otherwise select somebody else's vault.
|
|
229
|
-
const isAgentIdentity = options.claims?.["custom:entityType"] === "agent";
|
|
230
|
-
const agentSelfUid = options.claims?.["custom:entityUid"] ?? "";
|
|
231
|
-
const selfEntities = await options.client.entity.listByType(
|
|
232
|
-
isAgentIdentity ? "agent" : "person",
|
|
233
|
-
);
|
|
234
|
-
const pick = isAgentIdentity
|
|
235
|
-
? pickAgentSelfEntity(selfEntities, agentSelfUid)
|
|
236
|
-
: pickCanonicalPersonEntity(selfEntities);
|
|
237
|
-
if (pick?.bucketName) {
|
|
238
|
-
plan.push({
|
|
239
|
-
slug: "personal",
|
|
240
|
-
uid: pick.uid,
|
|
241
|
-
bucketName: pick.bucketName,
|
|
242
|
-
personalMode: true,
|
|
243
|
-
journalSlug: PERSONAL_VAULT_JOURNAL_SLUG,
|
|
244
|
-
});
|
|
245
|
-
} else if (options.personal) {
|
|
246
|
-
return { status: "setup-needed", reason: "no-person-entity" };
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return { status: "plan", plan, deferredUnresolved };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export function emitFanoutPlan(
|
|
254
|
-
emit: (event: RunnerEvent) => void,
|
|
255
|
-
plan: RunnerTarget[],
|
|
256
|
-
): void {
|
|
257
|
-
emit({ type: "fanout-plan", companies: plan });
|
|
258
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
3
|
-
rollupAllComplete,
|
|
4
|
-
type CompanyState,
|
|
5
|
-
} from "./sync-runner-rollup.js";
|
|
6
|
-
|
|
7
|
-
describe("rollupAllComplete", () => {
|
|
8
|
-
it("aggregates a very large changed-path set without using it as call arguments", () => {
|
|
9
|
-
const changedPathCount = 150_000;
|
|
10
|
-
const changedPaths = Array.from(
|
|
11
|
-
{ length: changedPathCount },
|
|
12
|
-
(_, index) => `workspace/session-${index}/state.json`,
|
|
13
|
-
);
|
|
14
|
-
const state: CompanyState = {
|
|
15
|
-
company: "personal",
|
|
16
|
-
status: "complete",
|
|
17
|
-
filesDownloaded: changedPathCount,
|
|
18
|
-
bytesDownloaded: 0,
|
|
19
|
-
filesTombstoned: 0,
|
|
20
|
-
scopeOrphansRemoved: 0,
|
|
21
|
-
changedPaths,
|
|
22
|
-
filesUploaded: 0,
|
|
23
|
-
bytesUploaded: 0,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const rollup = rollupAllComplete(
|
|
27
|
-
[{ uid: "prs_personal", slug: "personal", personalMode: true }],
|
|
28
|
-
new Map([["personal", state]]),
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
expect(rollup.changedPaths).toHaveLength(changedPathCount);
|
|
32
|
-
expect(rollup.changedPaths[0]).toBe("workspace/session-0/state.json");
|
|
33
|
-
expect(rollup.changedPaths.at(-1)).toBe(
|
|
34
|
-
`workspace/session-${changedPathCount - 1}/state.json`,
|
|
35
|
-
);
|
|
36
|
-
});
|
|
37
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { RunnerEvent } from "./sync-runner.js";
|
|
2
|
-
import type { RunnerTarget } from "./sync-runner-planning.js";
|
|
3
|
-
|
|
4
|
-
export type CompanyStatus = "complete" | "aborted" | "errored" | "transient-network";
|
|
5
|
-
|
|
6
|
-
export interface CompanyState {
|
|
7
|
-
company: string;
|
|
8
|
-
status: CompanyStatus;
|
|
9
|
-
filesDownloaded: number;
|
|
10
|
-
bytesDownloaded: number;
|
|
11
|
-
filesTombstoned: number;
|
|
12
|
-
scopeOrphansRemoved: number;
|
|
13
|
-
changedPaths: string[];
|
|
14
|
-
filesUploaded: number;
|
|
15
|
-
bytesUploaded: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AllCompleteRollup {
|
|
19
|
-
totalDownloaded: number;
|
|
20
|
-
totalDownloadedBytes: number;
|
|
21
|
-
totalTombstoned: number;
|
|
22
|
-
totalScopeOrphansRemoved: number;
|
|
23
|
-
totalUploaded: number;
|
|
24
|
-
totalUploadedBytes: number;
|
|
25
|
-
changedPaths: string[];
|
|
26
|
-
needsReindex: boolean;
|
|
27
|
-
partial: boolean;
|
|
28
|
-
companies: Extract<RunnerEvent, { type: "all-complete" }>["companies"];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function createCompanyState(company: string): CompanyState {
|
|
32
|
-
return {
|
|
33
|
-
company,
|
|
34
|
-
status: "errored",
|
|
35
|
-
filesDownloaded: 0,
|
|
36
|
-
bytesDownloaded: 0,
|
|
37
|
-
filesTombstoned: 0,
|
|
38
|
-
scopeOrphansRemoved: 0,
|
|
39
|
-
changedPaths: [],
|
|
40
|
-
filesUploaded: 0,
|
|
41
|
-
bytesUploaded: 0,
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function rollupAllComplete(
|
|
46
|
-
plan: RunnerTarget[],
|
|
47
|
-
stateByCompany: Map<string, CompanyState>,
|
|
48
|
-
): AllCompleteRollup {
|
|
49
|
-
let totalDownloaded = 0;
|
|
50
|
-
let totalDownloadedBytes = 0;
|
|
51
|
-
let totalTombstoned = 0;
|
|
52
|
-
let totalScopeOrphansRemoved = 0;
|
|
53
|
-
let totalUploaded = 0;
|
|
54
|
-
let totalUploadedBytes = 0;
|
|
55
|
-
const changedPaths: string[] = [];
|
|
56
|
-
let partial = false;
|
|
57
|
-
const companies: AllCompleteRollup["companies"] = [];
|
|
58
|
-
|
|
59
|
-
for (const target of plan) {
|
|
60
|
-
const s = stateByCompany.get(target.slug);
|
|
61
|
-
if (!s) continue;
|
|
62
|
-
totalDownloaded += s.filesDownloaded;
|
|
63
|
-
totalDownloadedBytes += s.bytesDownloaded;
|
|
64
|
-
totalTombstoned += s.filesTombstoned;
|
|
65
|
-
totalScopeOrphansRemoved += s.scopeOrphansRemoved;
|
|
66
|
-
totalUploaded += s.filesUploaded;
|
|
67
|
-
totalUploadedBytes += s.bytesUploaded;
|
|
68
|
-
for (const changedPath of s.changedPaths) {
|
|
69
|
-
changedPaths.push(changedPath);
|
|
70
|
-
}
|
|
71
|
-
if (s.status !== "complete") partial = true;
|
|
72
|
-
companies.push({
|
|
73
|
-
company: s.company,
|
|
74
|
-
status: s.status,
|
|
75
|
-
filesDownloaded: s.filesDownloaded,
|
|
76
|
-
bytesDownloaded: s.bytesDownloaded,
|
|
77
|
-
filesUploaded: s.filesUploaded,
|
|
78
|
-
bytesUploaded: s.bytesUploaded,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
totalDownloaded,
|
|
84
|
-
totalDownloadedBytes,
|
|
85
|
-
totalTombstoned,
|
|
86
|
-
totalScopeOrphansRemoved,
|
|
87
|
-
totalUploaded,
|
|
88
|
-
totalUploadedBytes,
|
|
89
|
-
changedPaths,
|
|
90
|
-
needsReindex:
|
|
91
|
-
totalDownloaded > 0 ||
|
|
92
|
-
totalTombstoned > 0 ||
|
|
93
|
-
totalScopeOrphansRemoved > 0,
|
|
94
|
-
partial,
|
|
95
|
-
companies,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export async function emitTelemetry(options: {
|
|
2
|
-
collectTelemetry?: () => Promise<void>;
|
|
3
|
-
defaultCollectTelemetry: () => Promise<void>;
|
|
4
|
-
onError?: (err: unknown) => void;
|
|
5
|
-
}): Promise<void> {
|
|
6
|
-
const telemetryFn =
|
|
7
|
-
options.collectTelemetry ?? options.defaultCollectTelemetry;
|
|
8
|
-
await telemetryFn().catch((err) => {
|
|
9
|
-
try {
|
|
10
|
-
options.onError?.(err);
|
|
11
|
-
} catch {
|
|
12
|
-
/* diagnostics must never affect sync */
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|