@indigoai-us/hq-cli 5.8.5 → 5.8.6
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/commands/files.js +94 -48
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/commands/files.ts +110 -50
- package/src/index.ts +1 -1
package/dist/commands/files.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a754383e-f129-5052-a8d8-59ea590b4f3d")}catch(e){}}();
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
5
5
|
import { vaultApiFetch, getCompanyUid } from "./secrets.js";
|
|
@@ -148,22 +148,36 @@ export function registerFilesCommand(program) {
|
|
|
148
148
|
const token = await ensureCognitoToken();
|
|
149
149
|
const companySlug = files.opts().company;
|
|
150
150
|
const companyUid = await getCompanyUid(token, companySlug);
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
151
|
+
// Fetch the prefix's own ACL row (creator, open flag, effective
|
|
152
|
+
// permission) and the inherited/descendant tree in parallel so the
|
|
153
|
+
// user sees every grant that affects this prefix in one shot.
|
|
154
|
+
const [aclRes, treeRes] = await Promise.all([
|
|
155
|
+
vaultApiFetch({
|
|
156
|
+
token,
|
|
157
|
+
path: `/files/${encodeURIComponent(companyUid)}/acl`,
|
|
158
|
+
query: { prefix: canonicalPrefix },
|
|
159
|
+
}),
|
|
160
|
+
vaultApiFetch({
|
|
161
|
+
token,
|
|
162
|
+
path: `/files/${encodeURIComponent(companyUid)}/acl/tree`,
|
|
163
|
+
query: { prefix: canonicalPrefix },
|
|
164
|
+
}),
|
|
165
|
+
]);
|
|
166
|
+
async function readErrorBody(res) {
|
|
167
|
+
return (await res.json().catch(() => ({})));
|
|
168
|
+
}
|
|
169
|
+
// Auth/server failures from either call are treated identically — bail
|
|
170
|
+
// out with a single message rather than printing a half-rendered view.
|
|
171
|
+
for (const res of [aclRes, treeRes]) {
|
|
172
|
+
if (res.ok || res.status === 404)
|
|
173
|
+
continue;
|
|
174
|
+
const body = await readErrorBody(res);
|
|
158
175
|
if (res.status === 401) {
|
|
159
176
|
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
160
177
|
}
|
|
161
178
|
else if (res.status === 403) {
|
|
162
179
|
console.error(chalk.red("Not authorized to view this file prefix's ACL"));
|
|
163
180
|
}
|
|
164
|
-
else if (res.status === 404) {
|
|
165
|
-
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
166
|
-
}
|
|
167
181
|
else if (res.status >= 500) {
|
|
168
182
|
console.error(chalk.red(`Server error: ${body.error ?? res.statusText}`));
|
|
169
183
|
}
|
|
@@ -172,46 +186,78 @@ export function registerFilesCommand(program) {
|
|
|
172
186
|
}
|
|
173
187
|
process.exit(1);
|
|
174
188
|
}
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
189
|
+
const acl = aclRes.ok ? (await aclRes.json()).acl : null;
|
|
190
|
+
const tree = treeRes.ok ? (await treeRes.json()) : null;
|
|
191
|
+
// No own row AND nothing inherited or granted below — original
|
|
192
|
+
// "no ACL record" exit path.
|
|
193
|
+
if (!acl && (!tree || (tree.inherited.length === 0 && tree.children.length === 0))) {
|
|
194
|
+
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
const aclPrefix = acl?.path ?? acl?.prefix ?? tree?.prefix ?? canonicalPrefix;
|
|
198
|
+
const aclStatus = acl?.open ? "open" : "restricted";
|
|
179
199
|
console.log(chalk.green(`ACL for ${aclPrefix} (${aclStatus})`));
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
200
|
+
if (acl) {
|
|
201
|
+
console.log(`Creator: ${acl.creatorUid}`);
|
|
202
|
+
if (acl.effectivePermission) {
|
|
203
|
+
console.log(`Your effective permission: ${acl.effectivePermission}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
console.log(chalk.gray("No direct ACL row — access flows from the inherited/descendant grants below."));
|
|
208
|
+
}
|
|
209
|
+
function printEntryTable(rows, showSource) {
|
|
210
|
+
const TYPE_W = Math.max(4, ...rows.map((e) => e.granteeType.length));
|
|
211
|
+
const GRANTEE_W = Math.max(7, ...rows.map((e) => e.granteeId.length));
|
|
212
|
+
const PERM_W = Math.max(10, ...rows.map((e) => e.permission.length));
|
|
213
|
+
const BY_W = Math.max(10, ...rows.map((e) => e.grantedBy.length));
|
|
214
|
+
const SRC_W = showSource
|
|
215
|
+
? Math.max(6, ...rows.map((e) => (e.sourcePrefix ?? "").length))
|
|
216
|
+
: 0;
|
|
217
|
+
const headerCols = [
|
|
218
|
+
"TYPE".padEnd(TYPE_W),
|
|
219
|
+
"GRANTEE".padEnd(GRANTEE_W),
|
|
220
|
+
"PERMISSION".padEnd(PERM_W),
|
|
221
|
+
"GRANTED_BY".padEnd(BY_W),
|
|
222
|
+
"GRANTED_AT",
|
|
223
|
+
];
|
|
224
|
+
if (showSource)
|
|
225
|
+
headerCols.splice(4, 0, "SOURCE".padEnd(SRC_W));
|
|
226
|
+
console.log(chalk.bold(headerCols.join(" ")));
|
|
227
|
+
for (const e of rows) {
|
|
228
|
+
const grantedAt = e.grantedAt.slice(0, 10);
|
|
229
|
+
const cols = [
|
|
230
|
+
e.granteeType.padEnd(TYPE_W),
|
|
231
|
+
e.granteeId.padEnd(GRANTEE_W),
|
|
232
|
+
e.permission.padEnd(PERM_W),
|
|
233
|
+
e.grantedBy.padEnd(BY_W),
|
|
234
|
+
grantedAt,
|
|
235
|
+
];
|
|
236
|
+
if (showSource)
|
|
237
|
+
cols.splice(4, 0, (e.sourcePrefix ?? "").padEnd(SRC_W));
|
|
238
|
+
console.log(cols.join(" "));
|
|
239
|
+
}
|
|
183
240
|
}
|
|
184
|
-
|
|
185
|
-
|
|
241
|
+
const directEntries = acl?.entries ?? tree?.direct ?? [];
|
|
242
|
+
if (directEntries.length === 0) {
|
|
243
|
+
if (acl?.open) {
|
|
186
244
|
console.log(chalk.gray("Open ACL — all active members have read access."));
|
|
187
245
|
}
|
|
188
|
-
else {
|
|
189
|
-
console.log(chalk.gray("No explicit grants — only creator has access."));
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"GRANTED_AT",
|
|
204
|
-
].join(" ");
|
|
205
|
-
console.log(chalk.bold(tableHeader));
|
|
206
|
-
for (const e of acl.entries) {
|
|
207
|
-
const grantedAt = e.grantedAt.slice(0, 10);
|
|
208
|
-
console.log([
|
|
209
|
-
e.granteeType.padEnd(TYPE_W),
|
|
210
|
-
e.granteeId.padEnd(GRANTEE_W),
|
|
211
|
-
e.permission.padEnd(PERM_W),
|
|
212
|
-
e.grantedBy.padEnd(BY_W),
|
|
213
|
-
grantedAt,
|
|
214
|
-
].join(" "));
|
|
246
|
+
else if (acl) {
|
|
247
|
+
console.log(chalk.gray("No explicit grants on this prefix — only creator has access."));
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
console.log("\nDirect entries (granted on this prefix):");
|
|
252
|
+
printEntryTable(directEntries, false);
|
|
253
|
+
}
|
|
254
|
+
if (tree && tree.inherited.length > 0) {
|
|
255
|
+
console.log("\nInherited (granted on an ancestor prefix):");
|
|
256
|
+
printEntryTable(tree.inherited, true);
|
|
257
|
+
}
|
|
258
|
+
if (tree && tree.children.length > 0) {
|
|
259
|
+
console.log("\nGranted on descendant prefixes (do not affect this prefix's access):");
|
|
260
|
+
printEntryTable(tree.children, true);
|
|
215
261
|
}
|
|
216
262
|
}
|
|
217
263
|
catch (err) {
|
|
@@ -221,4 +267,4 @@ export function registerFilesCommand(program) {
|
|
|
221
267
|
});
|
|
222
268
|
}
|
|
223
269
|
//# sourceMappingURL=files.js.map
|
|
224
|
-
//# debugId=
|
|
270
|
+
//# debugId=a754383e-f129-5052-a8d8-59ea590b4f3d
|
package/dist/index.js
CHANGED
|
@@ -30,7 +30,7 @@ const program = new Command();
|
|
|
30
30
|
program
|
|
31
31
|
.name("hq")
|
|
32
32
|
.description("HQ management CLI — modules, packages, and cloud sync")
|
|
33
|
-
.version("5.8.
|
|
33
|
+
.version("5.8.6");
|
|
34
34
|
// Module management subcommand group
|
|
35
35
|
const modulesCmd = program
|
|
36
36
|
.command("modules")
|
package/package.json
CHANGED
package/src/commands/files.ts
CHANGED
|
@@ -154,20 +154,35 @@ export function registerFilesCommand(program: Command): void {
|
|
|
154
154
|
const companySlug = files.opts().company as string | undefined;
|
|
155
155
|
const companyUid = await getCompanyUid(token, companySlug);
|
|
156
156
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
157
|
+
// Fetch the prefix's own ACL row (creator, open flag, effective
|
|
158
|
+
// permission) and the inherited/descendant tree in parallel so the
|
|
159
|
+
// user sees every grant that affects this prefix in one shot.
|
|
160
|
+
const [aclRes, treeRes] = await Promise.all([
|
|
161
|
+
vaultApiFetch({
|
|
162
|
+
token,
|
|
163
|
+
path: `/files/${encodeURIComponent(companyUid)}/acl`,
|
|
164
|
+
query: { prefix: canonicalPrefix },
|
|
165
|
+
}),
|
|
166
|
+
vaultApiFetch({
|
|
167
|
+
token,
|
|
168
|
+
path: `/files/${encodeURIComponent(companyUid)}/acl/tree`,
|
|
169
|
+
query: { prefix: canonicalPrefix },
|
|
170
|
+
}),
|
|
171
|
+
]);
|
|
162
172
|
|
|
163
|
-
|
|
164
|
-
|
|
173
|
+
async function readErrorBody(res: Response): Promise<Record<string, string>> {
|
|
174
|
+
return (await res.json().catch(() => ({}))) as Record<string, string>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Auth/server failures from either call are treated identically — bail
|
|
178
|
+
// out with a single message rather than printing a half-rendered view.
|
|
179
|
+
for (const res of [aclRes, treeRes]) {
|
|
180
|
+
if (res.ok || res.status === 404) continue;
|
|
181
|
+
const body = await readErrorBody(res);
|
|
165
182
|
if (res.status === 401) {
|
|
166
183
|
console.error(chalk.red("Not authenticated — please run `hq login`"));
|
|
167
184
|
} else if (res.status === 403) {
|
|
168
185
|
console.error(chalk.red("Not authorized to view this file prefix's ACL"));
|
|
169
|
-
} else if (res.status === 404) {
|
|
170
|
-
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
171
186
|
} else if (res.status >= 500) {
|
|
172
187
|
console.error(chalk.red(`Server error: ${body.error ?? res.statusText}`));
|
|
173
188
|
} else {
|
|
@@ -176,7 +191,14 @@ export function registerFilesCommand(program: Command): void {
|
|
|
176
191
|
process.exit(1);
|
|
177
192
|
}
|
|
178
193
|
|
|
179
|
-
|
|
194
|
+
type AclEntry = {
|
|
195
|
+
granteeType: string;
|
|
196
|
+
granteeId: string;
|
|
197
|
+
permission: string;
|
|
198
|
+
grantedBy: string;
|
|
199
|
+
grantedAt: string;
|
|
200
|
+
};
|
|
201
|
+
type AclResponse = {
|
|
180
202
|
acl: {
|
|
181
203
|
itemType: string;
|
|
182
204
|
companyUid: string;
|
|
@@ -186,60 +208,98 @@ export function registerFilesCommand(program: Command): void {
|
|
|
186
208
|
prefix?: string;
|
|
187
209
|
creatorUid: string;
|
|
188
210
|
open?: boolean;
|
|
189
|
-
entries:
|
|
190
|
-
granteeType: string;
|
|
191
|
-
granteeId: string;
|
|
192
|
-
permission: string;
|
|
193
|
-
grantedBy: string;
|
|
194
|
-
grantedAt: string;
|
|
195
|
-
}>;
|
|
211
|
+
entries: AclEntry[];
|
|
196
212
|
effectivePermission?: string | null;
|
|
197
213
|
createdAt: string;
|
|
198
214
|
updatedAt: string;
|
|
199
215
|
};
|
|
200
216
|
};
|
|
217
|
+
type TreeResponse = {
|
|
218
|
+
prefix: string;
|
|
219
|
+
direct: AclEntry[];
|
|
220
|
+
inherited: Array<AclEntry & { sourcePrefix: string }>;
|
|
221
|
+
children: Array<AclEntry & { sourcePrefix: string }>;
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const acl = aclRes.ok ? (await aclRes.json() as AclResponse).acl : null;
|
|
225
|
+
const tree = treeRes.ok ? (await treeRes.json()) as TreeResponse : null;
|
|
226
|
+
|
|
227
|
+
// No own row AND nothing inherited or granted below — original
|
|
228
|
+
// "no ACL record" exit path.
|
|
229
|
+
if (!acl && (!tree || (tree.inherited.length === 0 && tree.children.length === 0))) {
|
|
230
|
+
console.error(chalk.red(`No ACL record exists for '${canonicalPrefix}'`));
|
|
231
|
+
process.exit(1);
|
|
232
|
+
}
|
|
201
233
|
|
|
202
|
-
const
|
|
203
|
-
const aclStatus = acl
|
|
204
|
-
const aclPrefix = acl.path ?? acl.prefix ?? canonicalPrefix;
|
|
234
|
+
const aclPrefix = acl?.path ?? acl?.prefix ?? tree?.prefix ?? canonicalPrefix;
|
|
235
|
+
const aclStatus = acl?.open ? "open" : "restricted";
|
|
205
236
|
|
|
206
237
|
console.log(chalk.green(`ACL for ${aclPrefix} (${aclStatus})`));
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
238
|
+
if (acl) {
|
|
239
|
+
console.log(`Creator: ${acl.creatorUid}`);
|
|
240
|
+
if (acl.effectivePermission) {
|
|
241
|
+
console.log(`Your effective permission: ${acl.effectivePermission}`);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
console.log(chalk.gray(
|
|
245
|
+
"No direct ACL row — access flows from the inherited/descendant grants below.",
|
|
246
|
+
));
|
|
210
247
|
}
|
|
211
248
|
|
|
212
|
-
|
|
213
|
-
|
|
249
|
+
function printEntryTable(
|
|
250
|
+
rows: Array<AclEntry & { sourcePrefix?: string }>,
|
|
251
|
+
showSource: boolean,
|
|
252
|
+
): void {
|
|
253
|
+
const TYPE_W = Math.max(4, ...rows.map((e) => e.granteeType.length));
|
|
254
|
+
const GRANTEE_W = Math.max(7, ...rows.map((e) => e.granteeId.length));
|
|
255
|
+
const PERM_W = Math.max(10, ...rows.map((e) => e.permission.length));
|
|
256
|
+
const BY_W = Math.max(10, ...rows.map((e) => e.grantedBy.length));
|
|
257
|
+
const SRC_W = showSource
|
|
258
|
+
? Math.max(6, ...rows.map((e) => (e.sourcePrefix ?? "").length))
|
|
259
|
+
: 0;
|
|
260
|
+
const headerCols = [
|
|
261
|
+
"TYPE".padEnd(TYPE_W),
|
|
262
|
+
"GRANTEE".padEnd(GRANTEE_W),
|
|
263
|
+
"PERMISSION".padEnd(PERM_W),
|
|
264
|
+
"GRANTED_BY".padEnd(BY_W),
|
|
265
|
+
"GRANTED_AT",
|
|
266
|
+
];
|
|
267
|
+
if (showSource) headerCols.splice(4, 0, "SOURCE".padEnd(SRC_W));
|
|
268
|
+
console.log(chalk.bold(headerCols.join(" ")));
|
|
269
|
+
for (const e of rows) {
|
|
270
|
+
const grantedAt = e.grantedAt.slice(0, 10);
|
|
271
|
+
const cols = [
|
|
272
|
+
e.granteeType.padEnd(TYPE_W),
|
|
273
|
+
e.granteeId.padEnd(GRANTEE_W),
|
|
274
|
+
e.permission.padEnd(PERM_W),
|
|
275
|
+
e.grantedBy.padEnd(BY_W),
|
|
276
|
+
grantedAt,
|
|
277
|
+
];
|
|
278
|
+
if (showSource) cols.splice(4, 0, (e.sourcePrefix ?? "").padEnd(SRC_W));
|
|
279
|
+
console.log(cols.join(" "));
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const directEntries = acl?.entries ?? tree?.direct ?? [];
|
|
284
|
+
if (directEntries.length === 0) {
|
|
285
|
+
if (acl?.open) {
|
|
214
286
|
console.log(chalk.gray("Open ACL — all active members have read access."));
|
|
215
|
-
} else {
|
|
216
|
-
console.log(chalk.gray("No explicit grants — only creator has access."));
|
|
287
|
+
} else if (acl) {
|
|
288
|
+
console.log(chalk.gray("No explicit grants on this prefix — only creator has access."));
|
|
217
289
|
}
|
|
218
|
-
|
|
290
|
+
} else {
|
|
291
|
+
console.log("\nDirect entries (granted on this prefix):");
|
|
292
|
+
printEntryTable(directEntries, false);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (tree && tree.inherited.length > 0) {
|
|
296
|
+
console.log("\nInherited (granted on an ancestor prefix):");
|
|
297
|
+
printEntryTable(tree.inherited, true);
|
|
219
298
|
}
|
|
220
299
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const PERM_W = Math.max(10, ...acl.entries.map((e) => e.permission.length));
|
|
225
|
-
const BY_W = Math.max(10, ...acl.entries.map((e) => e.grantedBy.length));
|
|
226
|
-
const tableHeader = [
|
|
227
|
-
"TYPE".padEnd(TYPE_W),
|
|
228
|
-
"GRANTEE".padEnd(GRANTEE_W),
|
|
229
|
-
"PERMISSION".padEnd(PERM_W),
|
|
230
|
-
"GRANTED_BY".padEnd(BY_W),
|
|
231
|
-
"GRANTED_AT",
|
|
232
|
-
].join(" ");
|
|
233
|
-
console.log(chalk.bold(tableHeader));
|
|
234
|
-
for (const e of acl.entries) {
|
|
235
|
-
const grantedAt = e.grantedAt.slice(0, 10);
|
|
236
|
-
console.log([
|
|
237
|
-
e.granteeType.padEnd(TYPE_W),
|
|
238
|
-
e.granteeId.padEnd(GRANTEE_W),
|
|
239
|
-
e.permission.padEnd(PERM_W),
|
|
240
|
-
e.grantedBy.padEnd(BY_W),
|
|
241
|
-
grantedAt,
|
|
242
|
-
].join(" "));
|
|
300
|
+
if (tree && tree.children.length > 0) {
|
|
301
|
+
console.log("\nGranted on descendant prefixes (do not affect this prefix's access):");
|
|
302
|
+
printEntryTable(tree.children, true);
|
|
243
303
|
}
|
|
244
304
|
} catch (err) {
|
|
245
305
|
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
package/src/index.ts
CHANGED