@indigoai-us/hq-cli 5.108.26 → 5.109.1
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 +27 -0
- package/dist/commands/__fixtures__/access-vault.d.ts +93 -0
- package/dist/commands/__fixtures__/access-vault.js +166 -0
- package/dist/commands/access.d.ts +158 -0
- package/dist/commands/access.js +803 -0
- package/dist/commands/cloud.js +11 -1
- package/dist/commands/files-browse.d.ts +25 -1
- package/dist/commands/files-browse.js +81 -17
- package/dist/commands/files.js +15 -5
- package/dist/commands/sync-mode.js +12 -1
- package/dist/commands/sync-narrow.js +12 -1
- package/dist/register-all.js +3 -0
- package/dist/utils/access-denied-hint.d.ts +32 -0
- package/dist/utils/access-denied-hint.js +139 -0
- package/dist/utils/access-outcomes.d.ts +23 -0
- package/dist/utils/access-outcomes.js +53 -0
- package/dist/utils/access-requests.d.ts +28 -0
- package/dist/utils/access-requests.js +98 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.109.1] — 2026-09-08
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `hq access` records each outcome (outcome, company, timestamp only) to
|
|
10
|
+
`.hq/access-outcomes.jsonl` so the cause mix can be reviewed later.
|
|
11
|
+
|
|
12
|
+
## [5.109.0] — 2026-09-08
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- `hq access <path-or-query>` — one command for "I can't find or open this
|
|
17
|
+
file". Resolves an exact key or a fuzzy name (never auto-picks between
|
|
18
|
+
several hits), then reports exactly one of `never-existed`, `local`,
|
|
19
|
+
`not-synced`, or `no-access` (exit 0/2/3, 4 for ambiguous). When the file
|
|
20
|
+
exists and you can read it but it is not on disk, it fetches and pins it the
|
|
21
|
+
same way `hq files get` does, and if that fails it runs `hq sync status`,
|
|
22
|
+
`hq sync doctor`, and retries once. When you lack access it names the
|
|
23
|
+
grantor (ACL creator, then company owner/admin), shows their email, asks one
|
|
24
|
+
yes/no question, and sends them a DM whose Copy-prompt is the exact
|
|
25
|
+
`hq files share … --permission read` command. Repeat requests for the same
|
|
26
|
+
prefix are deduped for 24h via `.hq/access-requests.json`. `--json` without
|
|
27
|
+
`--yes` never sends; `--no-fix` diagnoses only.
|
|
28
|
+
- Every access-denied (403) error from `hq files …` and `hq sync …` now ends
|
|
29
|
+
with `Run: hq access <path>`; in a TTY it offers to run it for you.
|
|
30
|
+
Existing messages and exit codes are unchanged.
|
|
31
|
+
|
|
5
32
|
## [5.108.26] — 2026-09-08
|
|
6
33
|
|
|
7
34
|
### Added
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory vault / ACL / DM harness for the `hq access` command.
|
|
3
|
+
* No network I/O — listings, GetObject, STS vend, and DMs stay in process
|
|
4
|
+
* (or under a tmp hqRoot the caller supplies).
|
|
5
|
+
*/
|
|
6
|
+
import { vi } from "vitest";
|
|
7
|
+
import type { CompanyBrowseClientFactory, FilesBrowseVaultClient } from "../files-browse.js";
|
|
8
|
+
export type AccessVaultState = "missing" | "present-local" | "present-not-local" | "present-denied";
|
|
9
|
+
export interface FakeDmMessage {
|
|
10
|
+
recipient: string;
|
|
11
|
+
message: string;
|
|
12
|
+
prompt?: string;
|
|
13
|
+
details?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface FakeDmTransport {
|
|
16
|
+
sent: FakeDmMessage[];
|
|
17
|
+
send(msg: FakeDmMessage): Promise<{
|
|
18
|
+
eventId: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare function makeFakeDmTransport(): FakeDmTransport;
|
|
22
|
+
export interface AclEntry {
|
|
23
|
+
granteeType: string;
|
|
24
|
+
granteeId: string;
|
|
25
|
+
permission: string;
|
|
26
|
+
grantedBy: string;
|
|
27
|
+
grantedAt: string;
|
|
28
|
+
}
|
|
29
|
+
export interface AccessAclTree {
|
|
30
|
+
prefix: string;
|
|
31
|
+
direct: AclEntry[];
|
|
32
|
+
inherited: Array<AclEntry & {
|
|
33
|
+
sourcePrefix: string;
|
|
34
|
+
}>;
|
|
35
|
+
children: Array<AclEntry & {
|
|
36
|
+
sourcePrefix: string;
|
|
37
|
+
}>;
|
|
38
|
+
directRow: {
|
|
39
|
+
creatorUid: string;
|
|
40
|
+
open: boolean;
|
|
41
|
+
createdAt: string;
|
|
42
|
+
updatedAt: string;
|
|
43
|
+
} | null;
|
|
44
|
+
effectivePermission: string | null;
|
|
45
|
+
}
|
|
46
|
+
export interface AccessMember {
|
|
47
|
+
personUid: string;
|
|
48
|
+
personEmail: string;
|
|
49
|
+
personName?: string;
|
|
50
|
+
role: "owner" | "admin" | "member" | "guest";
|
|
51
|
+
}
|
|
52
|
+
export interface AccessVaultFixture {
|
|
53
|
+
state: AccessVaultState;
|
|
54
|
+
hqRoot: string;
|
|
55
|
+
companySlug: string;
|
|
56
|
+
companyUid: string;
|
|
57
|
+
/** Company-anchored e.g. companies/acme/knowledge/report.md */
|
|
58
|
+
key: string;
|
|
59
|
+
/** Company-relative e.g. knowledge/report.md */
|
|
60
|
+
bucketKey: string;
|
|
61
|
+
vaultClient: FilesBrowseVaultClient;
|
|
62
|
+
companyClient: CompanyBrowseClientFactory;
|
|
63
|
+
vend: ReturnType<typeof vi.fn>;
|
|
64
|
+
acl: AccessAclTree | null;
|
|
65
|
+
members: AccessMember[];
|
|
66
|
+
dm: FakeDmTransport;
|
|
67
|
+
listedKeys: string[];
|
|
68
|
+
region: string;
|
|
69
|
+
}
|
|
70
|
+
export interface MakeAccessFixtureOptions {
|
|
71
|
+
state: AccessVaultState;
|
|
72
|
+
hqRoot: string;
|
|
73
|
+
companySlug?: string;
|
|
74
|
+
key?: string;
|
|
75
|
+
extraKeys?: string[];
|
|
76
|
+
acl?: AccessAclTree | null;
|
|
77
|
+
members?: AccessMember[];
|
|
78
|
+
content?: string;
|
|
79
|
+
/**
|
|
80
|
+
* `present-denied` only: make the LIST call itself fail with 403 (the
|
|
81
|
+
* `/v1/files/list` shape) instead of returning the key. Models a caller
|
|
82
|
+
* without read on the prefix — the server refuses to enumerate.
|
|
83
|
+
*/
|
|
84
|
+
listDenied?: boolean;
|
|
85
|
+
/** Deny only the ROOT (prefix "") list — the caller can read the prefix but not enumerate the bucket. */
|
|
86
|
+
rootListDenied?: boolean;
|
|
87
|
+
}
|
|
88
|
+
export declare function vendStatusForState(state: AccessVaultState): {
|
|
89
|
+
status: number;
|
|
90
|
+
message?: string;
|
|
91
|
+
};
|
|
92
|
+
export declare function makeAccessFixture(opts: MakeAccessFixtureOptions): AccessVaultFixture;
|
|
93
|
+
//# sourceMappingURL=access-vault.d.ts.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory vault / ACL / DM harness for the `hq access` command.
|
|
3
|
+
* No network I/O — listings, GetObject, STS vend, and DMs stay in process
|
|
4
|
+
* (or under a tmp hqRoot the caller supplies).
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as path from "node:path";
|
|
8
|
+
import { Readable } from "node:stream";
|
|
9
|
+
import { vi } from "vitest";
|
|
10
|
+
import { ListObjectsV2Command, GetObjectCommand, } from "@aws-sdk/client-s3";
|
|
11
|
+
export function makeFakeDmTransport() {
|
|
12
|
+
const sent = [];
|
|
13
|
+
let n = 0;
|
|
14
|
+
return {
|
|
15
|
+
sent,
|
|
16
|
+
async send(msg) {
|
|
17
|
+
sent.push(msg);
|
|
18
|
+
n += 1;
|
|
19
|
+
return { eventId: `evt_${n}` };
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const FAKE_VEND = {
|
|
24
|
+
credentials: {
|
|
25
|
+
accessKeyId: "ASIA-test",
|
|
26
|
+
secretAccessKey: "secret-test",
|
|
27
|
+
sessionToken: "session-test",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const DEFAULT_MEMBERS = [
|
|
31
|
+
{
|
|
32
|
+
personUid: "prs_owner",
|
|
33
|
+
personEmail: "owner@example.com",
|
|
34
|
+
personName: "Olive Owner",
|
|
35
|
+
role: "owner",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
personUid: "prs_admin",
|
|
39
|
+
personEmail: "admin@example.com",
|
|
40
|
+
personName: "Adam Admin",
|
|
41
|
+
role: "admin",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
personUid: "prs_me",
|
|
45
|
+
personEmail: "me@example.com",
|
|
46
|
+
personName: "Mia Member",
|
|
47
|
+
role: "member",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
export function vendStatusForState(state) {
|
|
51
|
+
if (state === "missing")
|
|
52
|
+
return { status: 404 };
|
|
53
|
+
if (state === "present-denied") {
|
|
54
|
+
return { status: 403, message: "caller lacks read on this prefix" };
|
|
55
|
+
}
|
|
56
|
+
return { status: 200 };
|
|
57
|
+
}
|
|
58
|
+
function attachStatus(err, status) {
|
|
59
|
+
const tagged = err;
|
|
60
|
+
tagged.status = status;
|
|
61
|
+
return tagged;
|
|
62
|
+
}
|
|
63
|
+
function attachHttpStatus(err, httpStatusCode) {
|
|
64
|
+
const tagged = err;
|
|
65
|
+
tagged.$metadata = { httpStatusCode };
|
|
66
|
+
return tagged;
|
|
67
|
+
}
|
|
68
|
+
function toBucketKey(key, slug) {
|
|
69
|
+
const prefix = `companies/${slug}/`;
|
|
70
|
+
const trimmed = key.replace(/^\/+/, "");
|
|
71
|
+
if (trimmed.startsWith(prefix))
|
|
72
|
+
return trimmed.slice(prefix.length);
|
|
73
|
+
return trimmed;
|
|
74
|
+
}
|
|
75
|
+
export function makeAccessFixture(opts) {
|
|
76
|
+
const companySlug = opts.companySlug ?? "acme";
|
|
77
|
+
const key = opts.key ?? `companies/${companySlug}/knowledge/report.md`;
|
|
78
|
+
const bucketKey = toBucketKey(key, companySlug);
|
|
79
|
+
const companyUid = `cmp_${companySlug}`;
|
|
80
|
+
const content = opts.content ?? "hello";
|
|
81
|
+
const extraKeys = opts.extraKeys ?? [];
|
|
82
|
+
const listedKeys = opts.state === "missing" ? [...extraKeys] : [...extraKeys, bucketKey];
|
|
83
|
+
const members = opts.members ?? DEFAULT_MEMBERS;
|
|
84
|
+
const acl = opts.acl === undefined ? null : opts.acl;
|
|
85
|
+
const region = "us-east-1";
|
|
86
|
+
const dm = makeFakeDmTransport();
|
|
87
|
+
if (opts.state === "present-local") {
|
|
88
|
+
const dest = path.join(opts.hqRoot, key);
|
|
89
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
90
|
+
fs.writeFileSync(dest, content, "utf-8");
|
|
91
|
+
}
|
|
92
|
+
const vend = vi.fn(async () => {
|
|
93
|
+
if (opts.state === "missing") {
|
|
94
|
+
throw attachStatus(new Error("not found (404)"), 404);
|
|
95
|
+
}
|
|
96
|
+
if (opts.state === "present-denied") {
|
|
97
|
+
throw attachStatus(new Error("caller lacks read on this prefix (403)"), 403);
|
|
98
|
+
}
|
|
99
|
+
return FAKE_VEND;
|
|
100
|
+
});
|
|
101
|
+
const vendSelf = vi.fn(async () => FAKE_VEND);
|
|
102
|
+
const listMyExplicitGrants = vi.fn(async () => []);
|
|
103
|
+
const entityShape = {
|
|
104
|
+
uid: companyUid,
|
|
105
|
+
slug: companySlug,
|
|
106
|
+
name: companySlug,
|
|
107
|
+
bucketName: `hq-vault-cmp-${companySlug}`,
|
|
108
|
+
};
|
|
109
|
+
const findInMyNamespace = vi.fn(async () => entityShape);
|
|
110
|
+
const get = vi.fn(async () => entityShape);
|
|
111
|
+
const vaultClient = {
|
|
112
|
+
sts: { vend, vendSelf },
|
|
113
|
+
listMyExplicitGrants,
|
|
114
|
+
entity: { get, findInMyNamespace },
|
|
115
|
+
};
|
|
116
|
+
const listDenied = opts.state === "present-denied" && opts.listDenied === true;
|
|
117
|
+
const send = vi.fn(async (cmd) => {
|
|
118
|
+
if (cmd instanceof ListObjectsV2Command) {
|
|
119
|
+
const prefix = cmd.input.Prefix ?? "";
|
|
120
|
+
if (listDenied || (opts.rootListDenied === true && prefix === "")) {
|
|
121
|
+
throw Object.assign(new Error("files list failed (403)"), {
|
|
122
|
+
status: 403,
|
|
123
|
+
key: prefix,
|
|
124
|
+
company: companySlug,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
const Contents = listedKeys
|
|
128
|
+
.filter((k) => (prefix === "" ? true : k.startsWith(prefix)))
|
|
129
|
+
.map((Key) => ({
|
|
130
|
+
Key,
|
|
131
|
+
Size: content.length,
|
|
132
|
+
LastModified: new Date(),
|
|
133
|
+
}));
|
|
134
|
+
return { Contents };
|
|
135
|
+
}
|
|
136
|
+
if (cmd instanceof GetObjectCommand) {
|
|
137
|
+
const requested = cmd.input.Key ?? "";
|
|
138
|
+
if (opts.state === "missing" || requested !== bucketKey) {
|
|
139
|
+
throw attachHttpStatus(new Error("Not found (404)"), 404);
|
|
140
|
+
}
|
|
141
|
+
if (opts.state === "present-denied") {
|
|
142
|
+
throw attachHttpStatus(new Error("caller lacks read on this prefix"), 403);
|
|
143
|
+
}
|
|
144
|
+
return { Body: Readable.from([content]) };
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`unexpected command: ${cmd}`);
|
|
147
|
+
});
|
|
148
|
+
const companyClient = vi.fn(() => ({ send }));
|
|
149
|
+
return {
|
|
150
|
+
state: opts.state,
|
|
151
|
+
hqRoot: opts.hqRoot,
|
|
152
|
+
companySlug,
|
|
153
|
+
companyUid,
|
|
154
|
+
key,
|
|
155
|
+
bucketKey,
|
|
156
|
+
vaultClient,
|
|
157
|
+
companyClient,
|
|
158
|
+
vend,
|
|
159
|
+
acl,
|
|
160
|
+
members,
|
|
161
|
+
dm,
|
|
162
|
+
listedKeys,
|
|
163
|
+
region,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=access-vault.js.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `hq access <path-or-query>` — existence + ACL probe with a self-healing
|
|
3
|
+
* ladder: resolve, list, probe ACL, then (with --fix) download / pin / repair
|
|
4
|
+
* sync or request access from the grantor.
|
|
5
|
+
*
|
|
6
|
+
* Tells a teammate whether a vault file exists and whether they can read it,
|
|
7
|
+
* so they never see a bare not-found.
|
|
8
|
+
*
|
|
9
|
+
* Vending uses the multi-tenant STS route `vaultClient.sts.vend({ companyUid })`
|
|
10
|
+
* (`/sts/vend`). The legacy `POST /vend` is never used — it assumes a single
|
|
11
|
+
* static BUCKET_ARN that is unset in multi-tenant prod.
|
|
12
|
+
*
|
|
13
|
+
* Company vault keys are company-relative; the CLI speaks the anchored form
|
|
14
|
+
* `companies/<slug>/...` for display.
|
|
15
|
+
*/
|
|
16
|
+
import { Command } from "commander";
|
|
17
|
+
import { type CompanyBrowseClientFactory, type FilesBrowseVaultClient, type RunGetResult } from "./files-browse.js";
|
|
18
|
+
export type AccessOutcome = "never-existed" | "local" | "not-synced" | "no-access" | "ambiguous" | "pending-confirmation";
|
|
19
|
+
export interface AccessAclTree {
|
|
20
|
+
prefix: string;
|
|
21
|
+
direct: Array<{
|
|
22
|
+
granteeType: string;
|
|
23
|
+
granteeId: string;
|
|
24
|
+
permission: string;
|
|
25
|
+
grantedBy: string;
|
|
26
|
+
grantedAt: string;
|
|
27
|
+
}>;
|
|
28
|
+
inherited: Array<{
|
|
29
|
+
granteeType: string;
|
|
30
|
+
granteeId: string;
|
|
31
|
+
permission: string;
|
|
32
|
+
grantedBy: string;
|
|
33
|
+
grantedAt: string;
|
|
34
|
+
sourcePrefix: string;
|
|
35
|
+
}>;
|
|
36
|
+
children: Array<{
|
|
37
|
+
granteeType: string;
|
|
38
|
+
granteeId: string;
|
|
39
|
+
permission: string;
|
|
40
|
+
grantedBy: string;
|
|
41
|
+
grantedAt: string;
|
|
42
|
+
sourcePrefix: string;
|
|
43
|
+
}>;
|
|
44
|
+
directRow: {
|
|
45
|
+
creatorUid: string;
|
|
46
|
+
open: boolean;
|
|
47
|
+
createdAt: string;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
} | null;
|
|
50
|
+
effectivePermission: string | null;
|
|
51
|
+
}
|
|
52
|
+
export interface AccessMember {
|
|
53
|
+
personUid: string;
|
|
54
|
+
personEmail: string;
|
|
55
|
+
personName?: string;
|
|
56
|
+
role: "owner" | "admin" | "member" | "guest";
|
|
57
|
+
}
|
|
58
|
+
export interface AccessGrantor {
|
|
59
|
+
personUid: string;
|
|
60
|
+
email: string;
|
|
61
|
+
name?: string;
|
|
62
|
+
source: "acl-creator" | "owner" | "admin";
|
|
63
|
+
}
|
|
64
|
+
export interface AccessDeps {
|
|
65
|
+
hqRoot: string;
|
|
66
|
+
companySlug: string;
|
|
67
|
+
companyUid: string;
|
|
68
|
+
vaultClient: FilesBrowseVaultClient;
|
|
69
|
+
companyClient: CompanyBrowseClientFactory;
|
|
70
|
+
region: string;
|
|
71
|
+
/** GET acl/tree for the exact company-relative prefix; null = no ACL record. */
|
|
72
|
+
fetchAclTree: (prefix: string) => Promise<AccessAclTree | null>;
|
|
73
|
+
/** Active members (owner/admin/member/guest) — used for grantor resolution. */
|
|
74
|
+
listMembers: () => Promise<AccessMember[]>;
|
|
75
|
+
requester: {
|
|
76
|
+
email: string;
|
|
77
|
+
name?: string;
|
|
78
|
+
};
|
|
79
|
+
dm?: {
|
|
80
|
+
send(msg: {
|
|
81
|
+
recipient: string;
|
|
82
|
+
message: string;
|
|
83
|
+
prompt?: string;
|
|
84
|
+
details?: string;
|
|
85
|
+
}): Promise<{
|
|
86
|
+
eventId: string;
|
|
87
|
+
}>;
|
|
88
|
+
};
|
|
89
|
+
confirm?: (question: string) => Promise<boolean>;
|
|
90
|
+
now?: () => number;
|
|
91
|
+
/** Multi-hit chooser. Absent or returning null ⇒ outcome "ambiguous". Never auto-pick. */
|
|
92
|
+
pick?: (candidates: string[]) => Promise<string | null>;
|
|
93
|
+
/** Plain-text sink (default console.log). Tests capture it. */
|
|
94
|
+
log?: (line: string) => void;
|
|
95
|
+
/**
|
|
96
|
+
* Diagnostic sink (default console.error). Carries the recipient line before
|
|
97
|
+
* a DM is sent so it is visible even under --json, whose stdout must stay
|
|
98
|
+
* a single JSON object.
|
|
99
|
+
*/
|
|
100
|
+
stderr?: (line: string) => void;
|
|
101
|
+
/** Materialize + pin one path. Default: runGet from ./files-browse.js. */
|
|
102
|
+
get?: (anchoredPath: string) => Promise<RunGetResult>;
|
|
103
|
+
/** Summarise local sync journal state for the company. */
|
|
104
|
+
syncStatus?: () => Promise<string>;
|
|
105
|
+
/** Sync doctor. Default wraps hq-cloud syncDoctor(reconcileConflicts). */
|
|
106
|
+
syncDoctor?: (apply: boolean) => Promise<SyncDoctorOutcome>;
|
|
107
|
+
}
|
|
108
|
+
export interface SyncDoctorOutcome {
|
|
109
|
+
twins: number;
|
|
110
|
+
summary: string;
|
|
111
|
+
bulkRefused?: boolean;
|
|
112
|
+
breakerMessage?: string;
|
|
113
|
+
}
|
|
114
|
+
export interface RunAccessInput {
|
|
115
|
+
target: string;
|
|
116
|
+
fix: boolean;
|
|
117
|
+
json: boolean;
|
|
118
|
+
yes?: boolean;
|
|
119
|
+
deps: AccessDeps;
|
|
120
|
+
}
|
|
121
|
+
export interface RunAccessResult {
|
|
122
|
+
outcome: AccessOutcome;
|
|
123
|
+
path: string;
|
|
124
|
+
company: string;
|
|
125
|
+
exists: boolean;
|
|
126
|
+
grantor?: AccessGrantor;
|
|
127
|
+
candidates?: string[];
|
|
128
|
+
steps: string[];
|
|
129
|
+
exitCode: 0 | 2 | 3 | 4;
|
|
130
|
+
/** True when the self-heal get rung materialized the file. Omitted when not fixed. */
|
|
131
|
+
fixed?: boolean;
|
|
132
|
+
localPath?: string;
|
|
133
|
+
requestSentAt?: string;
|
|
134
|
+
alreadyAskedAt?: string;
|
|
135
|
+
requestNote?: string;
|
|
136
|
+
}
|
|
137
|
+
export declare function outcomeExitCode(outcome: AccessOutcome): 0 | 2 | 3 | 4;
|
|
138
|
+
export declare function formatAccessOutcome(result: RunAccessResult): string;
|
|
139
|
+
export declare function resolveGrantor(tree: AccessAclTree | null, members: AccessMember[]): AccessGrantor | null;
|
|
140
|
+
export declare function runAccess(input: RunAccessInput): Promise<RunAccessResult>;
|
|
141
|
+
export declare function registerAccessCommand(program: Command): void;
|
|
142
|
+
/**
|
|
143
|
+
* Resolve the company `hq access` runs against: explicit `company` (the
|
|
144
|
+
* ladder forwards the slug stamped on a 403), else the slug anchored in the
|
|
145
|
+
* target path, else the active company in `<hqRoot>/.hq/config.json`.
|
|
146
|
+
*/
|
|
147
|
+
export declare function resolveAccessCompany(target: string, opts?: {
|
|
148
|
+
company?: string;
|
|
149
|
+
hqRoot?: string;
|
|
150
|
+
}): string | undefined;
|
|
151
|
+
export declare function runAccessForPath(path: string, opts?: {
|
|
152
|
+
company?: string;
|
|
153
|
+
hqRoot?: string;
|
|
154
|
+
json?: boolean;
|
|
155
|
+
yes?: boolean;
|
|
156
|
+
fix?: boolean;
|
|
157
|
+
}): Promise<RunAccessResult>;
|
|
158
|
+
//# sourceMappingURL=access.d.ts.map
|