@bigstrider/transcodes-cli 0.4.0 → 0.5.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/README.md +4 -1
- package/dist/index.js +555 -109
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,13 +21,16 @@ Works the same on macOS, Linux, and Windows (Node ≥ 20).
|
|
|
21
21
|
|
|
22
22
|
| Command | What it does |
|
|
23
23
|
|---------|--------------|
|
|
24
|
-
| `transcodes` | Opens the dashboard
|
|
24
|
+
| `transcodes` | Opens the local dashboard (URL printed in the terminal; default port 3847, increments if busy) to paste, save, switch, label, or delete tokens (accepts `--port N` / `--no-open`). |
|
|
25
25
|
| `transcodes set <token> -l <label>` | Validates the JWT and saves it (label required) to `~/.transcodes/config.json` (dir `0700`, file `0600`), making it active. |
|
|
26
26
|
| `transcodes tokens` | Lists all saved tokens; the active one is marked with `*`. |
|
|
27
27
|
| `transcodes status` | Shows the active token source (env vs file) and its expiry. |
|
|
28
28
|
| `transcodes reset` | Deletes all saved tokens. |
|
|
29
|
+
| `transcodes policy refresh` | Force-refreshes the org policy bundle cache (same as MCP `refresh_rules`). |
|
|
29
30
|
| `transcodes help` | Shows the full command list and usage. |
|
|
30
31
|
|
|
32
|
+
Command descriptions are defined once in `cli/src/commands.ts` (SSOT) and shared with the dashboard CLI tab.
|
|
33
|
+
|
|
31
34
|
### Dashboard
|
|
32
35
|
|
|
33
36
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -345,7 +345,7 @@ function loadStepupConfig() {
|
|
|
345
345
|
}
|
|
346
346
|
const { token: tokenRaw } = resolveToken();
|
|
347
347
|
if (!tokenRaw) {
|
|
348
|
-
throw new Error("No Transcodes token found.
|
|
348
|
+
throw new Error("No Transcodes token found. Install the CLI (`npm install -g @bigstrider/transcodes-cli`), run `transcodes` to open the dashboard, and paste a token from the Transcodes console (member detail page, https://app.transcodes.io). Non-interactive: `transcodes set <token> -l <label>`. For CI only, set the TRANSCODES_TOKEN environment variable.");
|
|
349
349
|
}
|
|
350
350
|
const parsed = parseMemberAccessToken(tokenRaw);
|
|
351
351
|
for (const w of parsed.warnings) {
|
|
@@ -621,12 +621,11 @@ function normalizeRule(r) {
|
|
|
621
621
|
resource: coerceRbacResource(r.resource)
|
|
622
622
|
};
|
|
623
623
|
}
|
|
624
|
-
const provider = r.provider !== void 0 ? mapHostToProvider(r.provider) : void 0;
|
|
625
624
|
return {
|
|
626
625
|
...r,
|
|
627
626
|
type: "mcp",
|
|
628
627
|
matcher: r.matcher ?? "exact",
|
|
629
|
-
...provider !== void 0 ? { provider } : {},
|
|
628
|
+
...r.provider !== void 0 ? { provider: r.provider } : {},
|
|
630
629
|
...r.action !== void 0 ? { action: coerceRbacAction(r.action) } : {},
|
|
631
630
|
...r.resource !== void 0 ? { resource: coerceRbacResource(r.resource) } : {}
|
|
632
631
|
};
|
|
@@ -641,12 +640,6 @@ function loadMergedToolRules(bundleRules = []) {
|
|
|
641
640
|
}
|
|
642
641
|
return [...merged.values()];
|
|
643
642
|
}
|
|
644
|
-
function mapHostToProvider(host) {
|
|
645
|
-
if (!host)
|
|
646
|
-
return void 0;
|
|
647
|
-
const normalized = host === "claude-code" ? "claude" : host;
|
|
648
|
-
return isGuardProvider(normalized) ? normalized : void 0;
|
|
649
|
-
}
|
|
650
643
|
var ToolRuleValidationError = class extends Error {
|
|
651
644
|
};
|
|
652
645
|
function detectShellCommand(name) {
|
|
@@ -5153,6 +5146,66 @@ async function updateToolRule(id, changes) {
|
|
|
5153
5146
|
return merged;
|
|
5154
5147
|
}
|
|
5155
5148
|
|
|
5149
|
+
// src/commands.ts
|
|
5150
|
+
var CLI_COMMAND_SPECS = [
|
|
5151
|
+
{
|
|
5152
|
+
usage: "transcodes",
|
|
5153
|
+
description: "Open the local dashboard (URL printed in terminal; default port 3847; --port N / --no-open)"
|
|
5154
|
+
},
|
|
5155
|
+
{
|
|
5156
|
+
usage: "transcodes set <token> -l <label>",
|
|
5157
|
+
description: "Validate and save a member token with a required label, then make it active"
|
|
5158
|
+
},
|
|
5159
|
+
{
|
|
5160
|
+
usage: "transcodes reset",
|
|
5161
|
+
description: "Remove all saved tokens"
|
|
5162
|
+
},
|
|
5163
|
+
{
|
|
5164
|
+
usage: "transcodes status",
|
|
5165
|
+
description: "Show the active token source and expiry"
|
|
5166
|
+
},
|
|
5167
|
+
{
|
|
5168
|
+
usage: "transcodes tokens",
|
|
5169
|
+
description: "List all saved tokens (active one marked with *)"
|
|
5170
|
+
},
|
|
5171
|
+
{
|
|
5172
|
+
usage: "transcodes policy refresh",
|
|
5173
|
+
description: "Force-refresh the org policy bundle cache now"
|
|
5174
|
+
},
|
|
5175
|
+
{
|
|
5176
|
+
usage: "transcodes help",
|
|
5177
|
+
description: "Show the full command list and how to use each one"
|
|
5178
|
+
}
|
|
5179
|
+
];
|
|
5180
|
+
function escapeHtml(s) {
|
|
5181
|
+
return s.replace(
|
|
5182
|
+
/[&<>"]/g,
|
|
5183
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]
|
|
5184
|
+
);
|
|
5185
|
+
}
|
|
5186
|
+
function formatCliUsageFooter(configPath = transcodesConfigFile()) {
|
|
5187
|
+
return `The token is read by the transcodes-guard plugins/hooks with precedence:
|
|
5188
|
+
1. TRANSCODES_TOKEN environment variable (overrides everything)
|
|
5189
|
+
2. ${configPath}
|
|
5190
|
+
`;
|
|
5191
|
+
}
|
|
5192
|
+
function formatCliUsage(configPath = transcodesConfigFile()) {
|
|
5193
|
+
const usageLines = CLI_COMMAND_SPECS.map(
|
|
5194
|
+
(cmd) => ` ${cmd.usage.padEnd(34)}${cmd.description}`
|
|
5195
|
+
).join("\n");
|
|
5196
|
+
return `transcodes \u2014 transcodes-guard token manager
|
|
5197
|
+
|
|
5198
|
+
Usage:
|
|
5199
|
+
${usageLines}
|
|
5200
|
+
|
|
5201
|
+
${formatCliUsageFooter(configPath)}`;
|
|
5202
|
+
}
|
|
5203
|
+
function renderCliCommandsHtml() {
|
|
5204
|
+
return CLI_COMMAND_SPECS.filter((cmd) => cmd.showInDashboard !== false).map(
|
|
5205
|
+
(cmd) => `<div class="cmd"><code>${escapeHtml(cmd.usage)}</code><span class="cmd-desc">${escapeHtml(cmd.description)}</span></div>`
|
|
5206
|
+
).join("\n ");
|
|
5207
|
+
}
|
|
5208
|
+
|
|
5156
5209
|
// src/dashboard.ts
|
|
5157
5210
|
import { spawn } from "child_process";
|
|
5158
5211
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -5392,7 +5445,7 @@ var TRANSCODES_ADMIN_TOOLS = [
|
|
|
5392
5445
|
{
|
|
5393
5446
|
name: "update_member",
|
|
5394
5447
|
title: "Update member",
|
|
5395
|
-
description: "Update member fields (name, email,
|
|
5448
|
+
description: "Update member profile fields (name, email, metadata). Use update_member_role to change a role.",
|
|
5396
5449
|
category: "Members",
|
|
5397
5450
|
access: "api",
|
|
5398
5451
|
stepUpProtected: false
|
|
@@ -5465,7 +5518,7 @@ var TRANSCODES_ADMIN_TOOLS = [
|
|
|
5465
5518
|
{
|
|
5466
5519
|
name: "create_resource",
|
|
5467
5520
|
title: "Create resource",
|
|
5468
|
-
description: "Add a new RBAC resource key (
|
|
5521
|
+
description: "Add a new RBAC resource key (every role initialized to read=allow, write=allow+step-up).",
|
|
5469
5522
|
category: "RBAC",
|
|
5470
5523
|
access: "api",
|
|
5471
5524
|
stepUpProtected: false
|
|
@@ -5497,7 +5550,7 @@ var TRANSCODES_ADMIN_TOOLS = [
|
|
|
5497
5550
|
{
|
|
5498
5551
|
name: "update_member_role",
|
|
5499
5552
|
title: "Update member role",
|
|
5500
|
-
description: "Change a member's assigned role.",
|
|
5553
|
+
description: "Change a member's assigned role (validates the role exists).",
|
|
5501
5554
|
category: "RBAC",
|
|
5502
5555
|
access: "api",
|
|
5503
5556
|
stepUpProtected: true
|
|
@@ -5975,6 +6028,17 @@ function dashboardHtml() {
|
|
|
5975
6028
|
object-fit: contain;
|
|
5976
6029
|
background: #f4f4f6;
|
|
5977
6030
|
padding: 8px;
|
|
6031
|
+
display: block;
|
|
6032
|
+
}
|
|
6033
|
+
.header-logo-link {
|
|
6034
|
+
flex-shrink: 0;
|
|
6035
|
+
border-radius: 14px;
|
|
6036
|
+
line-height: 0;
|
|
6037
|
+
transition: opacity 0.15s ease, box-shadow 0.15s ease;
|
|
6038
|
+
}
|
|
6039
|
+
.header-logo-link:hover {
|
|
6040
|
+
opacity: 0.88;
|
|
6041
|
+
box-shadow: 0 0 0 3px var(--accent-soft);
|
|
5978
6042
|
}
|
|
5979
6043
|
.header h1 {
|
|
5980
6044
|
margin: 0;
|
|
@@ -5982,11 +6046,41 @@ function dashboardHtml() {
|
|
|
5982
6046
|
font-weight: 700;
|
|
5983
6047
|
letter-spacing: -0.02em;
|
|
5984
6048
|
}
|
|
5985
|
-
.header
|
|
5986
|
-
|
|
6049
|
+
.header-title-link {
|
|
6050
|
+
color: inherit;
|
|
6051
|
+
text-decoration: none;
|
|
6052
|
+
transition: color 0.15s ease;
|
|
6053
|
+
}
|
|
6054
|
+
.header-title-link:hover {
|
|
6055
|
+
color: var(--accent);
|
|
6056
|
+
}
|
|
6057
|
+
.header-title-row {
|
|
6058
|
+
display: flex;
|
|
6059
|
+
align-items: center;
|
|
6060
|
+
flex-wrap: wrap;
|
|
6061
|
+
gap: 8px 12px;
|
|
6062
|
+
}
|
|
6063
|
+
.header-tagline {
|
|
6064
|
+
margin: 6px 0 0;
|
|
5987
6065
|
font-size: var(--text-sm);
|
|
5988
6066
|
color: var(--muted);
|
|
5989
6067
|
}
|
|
6068
|
+
.header-status {
|
|
6069
|
+
display: inline-flex;
|
|
6070
|
+
align-items: center;
|
|
6071
|
+
gap: 8px;
|
|
6072
|
+
font-size: var(--text-2xs);
|
|
6073
|
+
font-weight: 600;
|
|
6074
|
+
color: var(--muted);
|
|
6075
|
+
}
|
|
6076
|
+
.status-dot {
|
|
6077
|
+
width: 8px;
|
|
6078
|
+
height: 8px;
|
|
6079
|
+
border-radius: 50%;
|
|
6080
|
+
background: #22c55e;
|
|
6081
|
+
flex-shrink: 0;
|
|
6082
|
+
box-shadow: 0 0 0 2px rgba(34, 197, 94, 0.22);
|
|
6083
|
+
}
|
|
5990
6084
|
.tabs {
|
|
5991
6085
|
display: flex;
|
|
5992
6086
|
gap: 4px;
|
|
@@ -6024,7 +6118,52 @@ function dashboardHtml() {
|
|
|
6024
6118
|
.section-sub {
|
|
6025
6119
|
font-size: var(--text-base);
|
|
6026
6120
|
color: var(--muted);
|
|
6027
|
-
margin: 0 0
|
|
6121
|
+
margin: 0 0 16px;
|
|
6122
|
+
}
|
|
6123
|
+
.cli-map-row {
|
|
6124
|
+
display: flex;
|
|
6125
|
+
align-items: center;
|
|
6126
|
+
flex-wrap: wrap;
|
|
6127
|
+
gap: 6px;
|
|
6128
|
+
margin: 0 0 16px;
|
|
6129
|
+
font-size: var(--text-2xs);
|
|
6130
|
+
font-weight: 400;
|
|
6131
|
+
color: var(--muted);
|
|
6132
|
+
}
|
|
6133
|
+
.cli-map-row .cli-map-label {
|
|
6134
|
+
font-size: var(--text-sm);
|
|
6135
|
+
font-weight: 600;
|
|
6136
|
+
letter-spacing: 0.01em;
|
|
6137
|
+
}
|
|
6138
|
+
.cli-map-row .cli-map-label--title {
|
|
6139
|
+
font-size: var(--text-lg);
|
|
6140
|
+
font-weight: 700;
|
|
6141
|
+
color: var(--ink);
|
|
6142
|
+
letter-spacing: -0.01em;
|
|
6143
|
+
}
|
|
6144
|
+
.cli-map-row .cli-map-label--danger {
|
|
6145
|
+
font-size: var(--text-sm);
|
|
6146
|
+
font-weight: 700;
|
|
6147
|
+
color: #c0392f;
|
|
6148
|
+
}
|
|
6149
|
+
.cli-map-row .cli-map-label--ink { color: var(--ink); }
|
|
6150
|
+
.cli-map-row code {
|
|
6151
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
6152
|
+
background: var(--accent-soft);
|
|
6153
|
+
color: var(--accent);
|
|
6154
|
+
padding: 2px 8px;
|
|
6155
|
+
border-radius: 6px;
|
|
6156
|
+
font-size: var(--text-2xs);
|
|
6157
|
+
font-weight: 600;
|
|
6158
|
+
}
|
|
6159
|
+
.cli-map-row--list {
|
|
6160
|
+
margin: 26px 0 10px;
|
|
6161
|
+
}
|
|
6162
|
+
.cli-map-row--section {
|
|
6163
|
+
margin: 0 0 4px;
|
|
6164
|
+
}
|
|
6165
|
+
.cli-map-row--danger {
|
|
6166
|
+
margin: 0;
|
|
6028
6167
|
}
|
|
6029
6168
|
textarea {
|
|
6030
6169
|
width: 100%;
|
|
@@ -6089,6 +6228,67 @@ function dashboardHtml() {
|
|
|
6089
6228
|
color: #5a5a64;
|
|
6090
6229
|
}
|
|
6091
6230
|
.btn-secondary:hover:not(:disabled) { background: #ececf0; }
|
|
6231
|
+
.btn-inline-action {
|
|
6232
|
+
display: inline-flex;
|
|
6233
|
+
align-items: center;
|
|
6234
|
+
gap: 6px;
|
|
6235
|
+
padding: 7px 14px;
|
|
6236
|
+
font-size: var(--text-2xs);
|
|
6237
|
+
font-weight: 600;
|
|
6238
|
+
color: var(--accent);
|
|
6239
|
+
background: var(--accent-soft);
|
|
6240
|
+
border: none;
|
|
6241
|
+
border-radius: 9px;
|
|
6242
|
+
cursor: pointer;
|
|
6243
|
+
transition: background 0.15s ease;
|
|
6244
|
+
}
|
|
6245
|
+
.btn-inline-action:hover:not(:disabled) { background: #e3e1f7; }
|
|
6246
|
+
.btn-inline-action:disabled { opacity: 0.55; cursor: default; }
|
|
6247
|
+
.danger-zone {
|
|
6248
|
+
display: flex;
|
|
6249
|
+
align-items: center;
|
|
6250
|
+
justify-content: space-between;
|
|
6251
|
+
gap: 16px;
|
|
6252
|
+
flex-wrap: wrap;
|
|
6253
|
+
margin-top: 28px;
|
|
6254
|
+
padding: 18px 20px;
|
|
6255
|
+
border: 1px solid #f0d9d6;
|
|
6256
|
+
border-radius: 16px;
|
|
6257
|
+
background: #fdf6f5;
|
|
6258
|
+
}
|
|
6259
|
+
.danger-zone-desc {
|
|
6260
|
+
margin: 4px 0 0;
|
|
6261
|
+
font-size: var(--text-2xs);
|
|
6262
|
+
color: #8a8a94;
|
|
6263
|
+
line-height: 1.5;
|
|
6264
|
+
}
|
|
6265
|
+
.btn-danger {
|
|
6266
|
+
padding: 11px 22px;
|
|
6267
|
+
font-size: var(--text-sm);
|
|
6268
|
+
font-weight: 700;
|
|
6269
|
+
color: #fff;
|
|
6270
|
+
background: #c0392f;
|
|
6271
|
+
border: none;
|
|
6272
|
+
border-radius: 11px;
|
|
6273
|
+
cursor: pointer;
|
|
6274
|
+
transition: background 0.15s ease;
|
|
6275
|
+
white-space: nowrap;
|
|
6276
|
+
}
|
|
6277
|
+
.btn-danger:hover:not(:disabled) { background: #a52f26; }
|
|
6278
|
+
.btn-danger:disabled { opacity: 0.55; cursor: default; }
|
|
6279
|
+
.policy-refresh-bar {
|
|
6280
|
+
display: flex;
|
|
6281
|
+
align-items: center;
|
|
6282
|
+
justify-content: space-between;
|
|
6283
|
+
gap: 12px;
|
|
6284
|
+
flex-wrap: wrap;
|
|
6285
|
+
margin-bottom: 18px;
|
|
6286
|
+
padding: 14px 16px;
|
|
6287
|
+
border: 1px solid var(--line);
|
|
6288
|
+
border-radius: 14px;
|
|
6289
|
+
background: #fbfbfc;
|
|
6290
|
+
}
|
|
6291
|
+
.policy-refresh-bar .cli-map-row { margin: 0; flex: 1; min-width: 0; }
|
|
6092
6292
|
.list-label {
|
|
6093
6293
|
margin: 26px 0 10px;
|
|
6094
6294
|
font-size: var(--text-sm);
|
|
@@ -6224,6 +6424,39 @@ function dashboardHtml() {
|
|
|
6224
6424
|
.perm-chip-1 { color: #166534; border-color: #86efac; background: #f0fdf4; }
|
|
6225
6425
|
.perm-chip-2 { color: #7c3aed; border-color: #d8b4fe; background: #faf5ff; }
|
|
6226
6426
|
.field-k-rbac { color: #8a3ffc; font-weight: 600; }
|
|
6427
|
+
.field-status {
|
|
6428
|
+
display: flex;
|
|
6429
|
+
align-items: center;
|
|
6430
|
+
flex-wrap: wrap;
|
|
6431
|
+
gap: 6px 10px;
|
|
6432
|
+
}
|
|
6433
|
+
.status-chip {
|
|
6434
|
+
display: inline-block;
|
|
6435
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
6436
|
+
font-size: var(--text-xs);
|
|
6437
|
+
font-weight: 600;
|
|
6438
|
+
padding: 1px 7px;
|
|
6439
|
+
border-radius: 6px;
|
|
6440
|
+
border: 1px solid;
|
|
6441
|
+
text-transform: lowercase;
|
|
6442
|
+
line-height: inherit;
|
|
6443
|
+
vertical-align: baseline;
|
|
6444
|
+
}
|
|
6445
|
+
.status-chip-active {
|
|
6446
|
+
color: #166534;
|
|
6447
|
+
border-color: #86efac;
|
|
6448
|
+
background: #f0fdf4;
|
|
6449
|
+
}
|
|
6450
|
+
.status-chip-inactive {
|
|
6451
|
+
color: #9f1239;
|
|
6452
|
+
border-color: #fda4af;
|
|
6453
|
+
background: #fff1f2;
|
|
6454
|
+
}
|
|
6455
|
+
.status-hint {
|
|
6456
|
+
font-size: var(--text-2xs);
|
|
6457
|
+
color: var(--muted);
|
|
6458
|
+
line-height: 1.45;
|
|
6459
|
+
}
|
|
6227
6460
|
.admin-tools-count {
|
|
6228
6461
|
font-size: var(--text-sm);
|
|
6229
6462
|
color: var(--muted);
|
|
@@ -6412,6 +6645,40 @@ function dashboardHtml() {
|
|
|
6412
6645
|
flex-direction: column;
|
|
6413
6646
|
gap: 10px;
|
|
6414
6647
|
}
|
|
6648
|
+
.guide-groups {
|
|
6649
|
+
display: flex;
|
|
6650
|
+
flex-direction: column;
|
|
6651
|
+
gap: 22px;
|
|
6652
|
+
}
|
|
6653
|
+
.guide-group-label {
|
|
6654
|
+
margin: 0 0 10px;
|
|
6655
|
+
font-size: var(--text-sm);
|
|
6656
|
+
font-weight: 700;
|
|
6657
|
+
color: var(--ink);
|
|
6658
|
+
letter-spacing: 0.01em;
|
|
6659
|
+
}
|
|
6660
|
+
a.guide-group-label {
|
|
6661
|
+
display: inline-flex;
|
|
6662
|
+
align-items: center;
|
|
6663
|
+
gap: 6px;
|
|
6664
|
+
text-decoration: none;
|
|
6665
|
+
color: var(--ink);
|
|
6666
|
+
}
|
|
6667
|
+
a.guide-group-label:hover {
|
|
6668
|
+
color: var(--accent);
|
|
6669
|
+
}
|
|
6670
|
+
a.guide-group-label:hover .guide-group-link-icon {
|
|
6671
|
+
color: var(--accent);
|
|
6672
|
+
}
|
|
6673
|
+
.guide-group-link-icon {
|
|
6674
|
+
display: inline-flex;
|
|
6675
|
+
align-items: center;
|
|
6676
|
+
color: var(--muted);
|
|
6677
|
+
line-height: 0;
|
|
6678
|
+
transition: color 0.15s ease;
|
|
6679
|
+
}
|
|
6680
|
+
.guide-group--panel .guide-group-label { color: var(--accent); }
|
|
6681
|
+
.guide-group--agent .guide-group-label { color: #5a5a64; }
|
|
6415
6682
|
.guide-step {
|
|
6416
6683
|
display: flex;
|
|
6417
6684
|
gap: 14px;
|
|
@@ -6455,6 +6722,15 @@ function dashboardHtml() {
|
|
|
6455
6722
|
padding: 1px 6px;
|
|
6456
6723
|
border-radius: 6px;
|
|
6457
6724
|
}
|
|
6725
|
+
.guide-step-desc-row {
|
|
6726
|
+
display: flex;
|
|
6727
|
+
align-items: center;
|
|
6728
|
+
flex-wrap: wrap;
|
|
6729
|
+
gap: 8px 12px;
|
|
6730
|
+
}
|
|
6731
|
+
.guide-step .toast {
|
|
6732
|
+
margin-top: 10px;
|
|
6733
|
+
}
|
|
6458
6734
|
.guide-help {
|
|
6459
6735
|
margin: 0 0 18px;
|
|
6460
6736
|
padding: 16px 18px;
|
|
@@ -6506,10 +6782,18 @@ function dashboardHtml() {
|
|
|
6506
6782
|
<body>
|
|
6507
6783
|
<div class="card">
|
|
6508
6784
|
<div class="header">
|
|
6509
|
-
<
|
|
6785
|
+
<a class="header-logo-link" href="https://app.transcodes.io/" target="_blank" rel="noopener noreferrer" aria-label="Open Transcodes console">
|
|
6786
|
+
<img class="avatar" src="${LOGO_DATA_URI}" alt="Transcodes" />
|
|
6787
|
+
</a>
|
|
6510
6788
|
<div>
|
|
6511
|
-
<
|
|
6512
|
-
|
|
6789
|
+
<div class="header-title-row">
|
|
6790
|
+
<h1><a class="header-title-link" href="https://app.transcodes.io/" target="_blank" rel="noopener noreferrer">Transcodes</a> CLI Panel</h1>
|
|
6791
|
+
<span class="header-status">
|
|
6792
|
+
<span class="status-dot" aria-hidden="true"></span>
|
|
6793
|
+
Connected
|
|
6794
|
+
</span>
|
|
6795
|
+
</div>
|
|
6796
|
+
<p class="header-tagline">Manage credentials and rules from one panel \u2014 no CLI typing required</p>
|
|
6513
6797
|
</div>
|
|
6514
6798
|
</div>
|
|
6515
6799
|
<div class="tabs">
|
|
@@ -6536,61 +6820,104 @@ function dashboardHtml() {
|
|
|
6536
6820
|
></mux-player>
|
|
6537
6821
|
</div>
|
|
6538
6822
|
<p class="list-label">Quick setup</p>
|
|
6539
|
-
<
|
|
6540
|
-
<
|
|
6541
|
-
<span class="guide-
|
|
6542
|
-
<
|
|
6543
|
-
<
|
|
6544
|
-
|
|
6545
|
-
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
<
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6554
|
-
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
<
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
<
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
<
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6823
|
+
<div class="guide-groups">
|
|
6824
|
+
<section class="guide-group guide-group--console">
|
|
6825
|
+
<a class="guide-group-label" href="https://app.transcodes.io/" target="_blank" rel="noopener noreferrer">Transcodes Console<span class="guide-group-link-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span></a>
|
|
6826
|
+
<ol class="guide-steps">
|
|
6827
|
+
<li class="guide-step">
|
|
6828
|
+
<span class="guide-step-num">1</span>
|
|
6829
|
+
<div class="guide-step-body">
|
|
6830
|
+
<p class="guide-step-title">Create a project</p>
|
|
6831
|
+
<p class="guide-step-desc">Create a new project for your app</p>
|
|
6832
|
+
</div>
|
|
6833
|
+
</li>
|
|
6834
|
+
<li class="guide-step">
|
|
6835
|
+
<span class="guide-step-num">2</span>
|
|
6836
|
+
<div class="guide-step-body">
|
|
6837
|
+
<p class="guide-step-title">Create an authentication cluster</p>
|
|
6838
|
+
<p class="guide-step-desc">Add an authentication cluster to define how members sign in and authenticate</p>
|
|
6839
|
+
</div>
|
|
6840
|
+
</li>
|
|
6841
|
+
<li class="guide-step">
|
|
6842
|
+
<span class="guide-step-num">3</span>
|
|
6843
|
+
<div class="guide-step-body">
|
|
6844
|
+
<p class="guide-step-title">Add a member</p>
|
|
6845
|
+
<p class="guide-step-desc">Invite or add a member so they can be issued an access token</p>
|
|
6846
|
+
</div>
|
|
6847
|
+
</li>
|
|
6848
|
+
<li class="guide-step">
|
|
6849
|
+
<span class="guide-step-num">4</span>
|
|
6850
|
+
<div class="guide-step-body">
|
|
6851
|
+
<p class="guide-step-title">Issue an access token</p>
|
|
6852
|
+
<p class="guide-step-desc">Open the member detail page and issue a Member Access Token (MAT) for the agent</p>
|
|
6853
|
+
</div>
|
|
6854
|
+
</li>
|
|
6855
|
+
</ol>
|
|
6856
|
+
</section>
|
|
6857
|
+
|
|
6858
|
+
<section class="guide-group guide-group--panel">
|
|
6859
|
+
<p class="guide-group-label">This CLI panel</p>
|
|
6860
|
+
<ol class="guide-steps">
|
|
6861
|
+
<li class="guide-step">
|
|
6862
|
+
<span class="guide-step-num">5</span>
|
|
6863
|
+
<div class="guide-step-body">
|
|
6864
|
+
<p class="guide-step-title">Save the token</p>
|
|
6865
|
+
<p class="guide-step-desc">Paste the token in the Tokens tab with a label (e.g. <code>transcodes-myapp-dev</code>) \u2014 the plugin reads it from <code>{{HOME_DIR}}/.transcodes/config.json</code></p>
|
|
6866
|
+
</div>
|
|
6867
|
+
</li>
|
|
6868
|
+
</ol>
|
|
6869
|
+
</section>
|
|
6870
|
+
|
|
6871
|
+
<section class="guide-group guide-group--agent">
|
|
6872
|
+
<p class="guide-group-label">LLM agent (Claude, Cursor, Codex, Antigravity)</p>
|
|
6873
|
+
<ol class="guide-steps">
|
|
6874
|
+
<li class="guide-step">
|
|
6875
|
+
<span class="guide-step-num">6</span>
|
|
6876
|
+
<div class="guide-step-body">
|
|
6877
|
+
<p class="guide-step-title">Ask your local agent to add a custom rule</p>
|
|
6878
|
+
<p class="guide-step-desc">Use <code>add_tool_rule</code> or <code>add_user_pattern</code> \u2014 on first registration, rules are <code>inactive</code> and not applied to MCP</p>
|
|
6879
|
+
</div>
|
|
6880
|
+
</li>
|
|
6881
|
+
</ol>
|
|
6882
|
+
</section>
|
|
6883
|
+
|
|
6884
|
+
<section class="guide-group guide-group--console">
|
|
6885
|
+
<a class="guide-group-label" href="https://app.transcodes.io/" target="_blank" rel="noopener noreferrer">Transcodes Console<span class="guide-group-link-icon" aria-hidden="true"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></span></a>
|
|
6886
|
+
<ol class="guide-steps">
|
|
6887
|
+
<li class="guide-step">
|
|
6888
|
+
<span class="guide-step-num">7</span>
|
|
6889
|
+
<div class="guide-step-body">
|
|
6890
|
+
<p class="guide-step-title">Approve the rule</p>
|
|
6891
|
+
<p class="guide-step-desc">Switch the rule to <code>active</code> \u2014 only then is it enforced on MCP</p>
|
|
6892
|
+
</div>
|
|
6893
|
+
</li>
|
|
6894
|
+
</ol>
|
|
6895
|
+
</section>
|
|
6896
|
+
|
|
6897
|
+
<section class="guide-group guide-group--panel">
|
|
6898
|
+
<p class="guide-group-label">This CLI panel</p>
|
|
6899
|
+
<ol class="guide-steps">
|
|
6900
|
+
<li class="guide-step">
|
|
6901
|
+
<span class="guide-step-num">8</span>
|
|
6902
|
+
<div class="guide-step-body">
|
|
6903
|
+
<p class="guide-step-title">Refresh active rules locally</p>
|
|
6904
|
+
<p class="guide-step-desc guide-step-desc-row">
|
|
6905
|
+
Run terminal <code>transcodes policy refresh</code> or
|
|
6906
|
+
<button type="button" class="btn-inline-action" id="guide-policy-refresh">Refresh</button>
|
|
6907
|
+
</p>
|
|
6908
|
+
<div id="guide-toast" class="toast"></div>
|
|
6909
|
+
</div>
|
|
6910
|
+
</li>
|
|
6911
|
+
</ol>
|
|
6912
|
+
</section>
|
|
6913
|
+
</div>
|
|
6590
6914
|
</div>
|
|
6591
6915
|
|
|
6592
6916
|
<div class="panel" id="panel-tokens">
|
|
6593
|
-
<p class="section
|
|
6917
|
+
<p class="cli-map-row cli-map-row--section">
|
|
6918
|
+
<span class="cli-map-label cli-map-label--title">MCP Access Token</span>
|
|
6919
|
+
<code>transcodes set <token> -l <label></code>
|
|
6920
|
+
</p>
|
|
6594
6921
|
<p class="section-sub">Paste the token from your Transcodes console member detail page</p>
|
|
6595
6922
|
<textarea id="token" placeholder="eyJhbGciOi\u2026" spellcheck="false" autocomplete="off"></textarea>
|
|
6596
6923
|
<input type="text" id="label" class="label-input" placeholder="Label (required) \u2014 e.g. transcodes-{project_name}-{env}" autocomplete="off" required />
|
|
@@ -6599,8 +6926,21 @@ function dashboardHtml() {
|
|
|
6599
6926
|
<button type="button" class="btn-secondary" id="clear">Clear</button>
|
|
6600
6927
|
</div>
|
|
6601
6928
|
<div id="toast" class="toast"></div>
|
|
6602
|
-
<p class="list
|
|
6929
|
+
<p class="cli-map-row cli-map-row--list">
|
|
6930
|
+
<span class="cli-map-label">Saved tokens</span>
|
|
6931
|
+
<code>transcodes tokens</code>
|
|
6932
|
+
</p>
|
|
6603
6933
|
<div class="token-list" id="token-list"></div>
|
|
6934
|
+
<div class="danger-zone">
|
|
6935
|
+
<div class="danger-zone-text">
|
|
6936
|
+
<p class="cli-map-row cli-map-row--danger">
|
|
6937
|
+
<span class="cli-map-label cli-map-label--danger">Reset all tokens</span>
|
|
6938
|
+
<code>transcodes reset</code>
|
|
6939
|
+
</p>
|
|
6940
|
+
<p class="danger-zone-desc">Remove every saved token from this machine</p>
|
|
6941
|
+
</div>
|
|
6942
|
+
<button type="button" class="btn-danger" id="reset-all">Reset all</button>
|
|
6943
|
+
</div>
|
|
6604
6944
|
<p class="hint">Saved to <code>{{HOME_DIR}}/.transcodes/config.json</code><br />Press Ctrl+C in the terminal to stop</p>
|
|
6605
6945
|
</div>
|
|
6606
6946
|
|
|
@@ -6608,22 +6948,18 @@ function dashboardHtml() {
|
|
|
6608
6948
|
<p class="section-title">CLI Commands</p>
|
|
6609
6949
|
<p class="section-sub">Run these from your terminal \u2014 the dashboard wraps the same actions</p>
|
|
6610
6950
|
<div class="cmd-list">
|
|
6611
|
-
|
|
6612
|
-
<div class="cmd"><code>transcodes set <token> -l <label></code><span class="cmd-desc">Validate and save a token with a label, then make it active</span></div>
|
|
6613
|
-
<div class="cmd"><code>transcodes tokens</code><span class="cmd-desc">List all saved tokens (active one marked with *)</span></div>
|
|
6614
|
-
<div class="cmd"><code>transcodes reset</code><span class="cmd-desc">Remove all saved tokens</span></div>
|
|
6615
|
-
<div class="cmd"><code>transcodes help</code><span class="cmd-desc">Show the full command list and how to use each one</span></div>
|
|
6951
|
+
${renderCliCommandsHtml()}
|
|
6616
6952
|
</div>
|
|
6617
6953
|
</div>
|
|
6618
6954
|
|
|
6619
6955
|
<div class="panel" id="panel-rules">
|
|
6620
6956
|
<p id="policy-token-warning" class="policy-token-warning" hidden>transcodes\uB97C \uB85C\uCEEC \uC5D0\uC774\uC804\uD2B8\uC5D0\uC11C \uC774\uC6A9\uD558\uAE30 \uC704\uD574\uC120 \uD1A0\uD070\uC774 \uD558\uB098 \uC774\uC0C1 \uB4F1\uB85D\uC774 \uB418\uC5B4\uC57C \uD569\uB2C8\uB2E4</p>
|
|
6621
6957
|
<div class="tabs sub-tabs" role="tablist" aria-label="Rule type">
|
|
6622
|
-
<button type="button" class="tab active" data-policy="project" role="tab">Project
|
|
6623
|
-
<button type="button" class="tab" data-policy="admin" role="tab">
|
|
6958
|
+
<button type="button" class="tab active" data-policy="project" role="tab">Project</button>
|
|
6959
|
+
<button type="button" class="tab" data-policy="admin" role="tab">Transcodes</button>
|
|
6624
6960
|
</div>
|
|
6625
6961
|
<div class="policy-pane active" id="policy-pane-project">
|
|
6626
|
-
<p class="section-title">Step-up
|
|
6962
|
+
<p class="section-title">Custom Project Step-up Rules</p>
|
|
6627
6963
|
<p class="section-sub">When an active rule matches an agent action, Transcodes step-up authentication is triggered. Rules are registered only from your local agent; activating and deleting them is done in the Transcodes web console</p>
|
|
6628
6964
|
<div class="usage">
|
|
6629
6965
|
<p class="usage-title">How rules work</p>
|
|
@@ -6639,15 +6975,22 @@ function dashboardHtml() {
|
|
|
6639
6975
|
<span class="q">\u2192 the agent registers it as <code>inactive</code>; activate it in the Transcodes web console to start enforcing</span>
|
|
6640
6976
|
</div>
|
|
6641
6977
|
</div>
|
|
6978
|
+
<div class="policy-refresh-bar">
|
|
6979
|
+
<p class="cli-map-row">
|
|
6980
|
+
<span class="cli-map-label cli-map-label--ink">Refresh Custom Project Rules</span>
|
|
6981
|
+
<code>transcodes policy refresh</code>
|
|
6982
|
+
</p>
|
|
6983
|
+
<button type="button" class="btn-inline-action" id="policy-refresh">Refresh</button>
|
|
6984
|
+
</div>
|
|
6642
6985
|
<div id="rules-toast" class="toast"></div>
|
|
6643
|
-
<p class="list-label">Your
|
|
6986
|
+
<p class="list-label">Your Project MCP Rules</p>
|
|
6644
6987
|
<div class="token-list" id="project-rules-list"></div>
|
|
6645
6988
|
<p class="list-label">System bash patterns (read-only)</p>
|
|
6646
6989
|
<div class="token-list" id="system-patterns-list"></div>
|
|
6647
6990
|
</div>
|
|
6648
6991
|
|
|
6649
6992
|
<div class="policy-pane" id="policy-pane-admin">
|
|
6650
|
-
<p class="section-title">Transcodes
|
|
6993
|
+
<p class="section-title">Transcodes MCP Rules</p>
|
|
6651
6994
|
<p class="section-sub">Backend API tools exposed via MCP \u2014 agents call these through the transcodes-guard plugin. Read-only reference for system tool-rules.</p>
|
|
6652
6995
|
<div class="rbac-legend">
|
|
6653
6996
|
<p class="rbac-legend-title">Role permission check</p>
|
|
@@ -6886,6 +7229,28 @@ function dashboardHtml() {
|
|
|
6886
7229
|
tokenEl.focus();
|
|
6887
7230
|
});
|
|
6888
7231
|
|
|
7232
|
+
const resetAllBtn = document.getElementById("reset-all");
|
|
7233
|
+
resetAllBtn.addEventListener("click", async () => {
|
|
7234
|
+
const count = (lastStatus.tokens || []).length;
|
|
7235
|
+
if (count === 0) {
|
|
7236
|
+
showToast("No tokens to reset", "error");
|
|
7237
|
+
return;
|
|
7238
|
+
}
|
|
7239
|
+
if (!confirm("Remove ALL " + count + " saved token(s)? This cannot be undone.")) return;
|
|
7240
|
+
resetAllBtn.disabled = true;
|
|
7241
|
+
try {
|
|
7242
|
+
const res = await fetch("/api/tokens", { method: "DELETE" });
|
|
7243
|
+
const data = await res.json();
|
|
7244
|
+
if (!res.ok) throw new Error(data.error || "Reset failed");
|
|
7245
|
+
showToast("All tokens removed", "success");
|
|
7246
|
+
refresh();
|
|
7247
|
+
} catch (e) {
|
|
7248
|
+
showToast(e.message || "Reset failed", "error");
|
|
7249
|
+
} finally {
|
|
7250
|
+
resetAllBtn.disabled = false;
|
|
7251
|
+
}
|
|
7252
|
+
});
|
|
7253
|
+
|
|
6889
7254
|
const rulesToastEl = document.getElementById("rules-toast");
|
|
6890
7255
|
const projectRulesListEl = document.getElementById("project-rules-list");
|
|
6891
7256
|
const systemPatternsListEl = document.getElementById("system-patterns-list");
|
|
@@ -6915,6 +7280,41 @@ function dashboardHtml() {
|
|
|
6915
7280
|
);
|
|
6916
7281
|
}
|
|
6917
7282
|
|
|
7283
|
+
function ruleStatusField(status) {
|
|
7284
|
+
if (!status) return "";
|
|
7285
|
+
const chipClass =
|
|
7286
|
+
status === "active"
|
|
7287
|
+
? "status-chip-active"
|
|
7288
|
+
: status === "inactive"
|
|
7289
|
+
? "status-chip-inactive"
|
|
7290
|
+
: "";
|
|
7291
|
+
const chip = chipClass
|
|
7292
|
+
? '<span class="status-chip ' + chipClass + '">' + esc(status) + "</span>"
|
|
7293
|
+
: "<code>" + esc(status) + "</code>";
|
|
7294
|
+
const hint =
|
|
7295
|
+
status === "inactive"
|
|
7296
|
+
? '<span class="status-hint">Activate in the console to take effect on MCP</span>'
|
|
7297
|
+
: "";
|
|
7298
|
+
return (
|
|
7299
|
+
'<div class="field field-status">' +
|
|
7300
|
+
'<span class="k">status</span> ' +
|
|
7301
|
+
chip +
|
|
7302
|
+
hint +
|
|
7303
|
+
"</div>"
|
|
7304
|
+
);
|
|
7305
|
+
}
|
|
7306
|
+
|
|
7307
|
+
function ruleResourceActionField(r) {
|
|
7308
|
+
const resource = r.resource || "\u2014";
|
|
7309
|
+
const action = r.action || "\u2014";
|
|
7310
|
+
return (
|
|
7311
|
+
'<div class="field">' +
|
|
7312
|
+
'<span class="k">resource / action</span> ' +
|
|
7313
|
+
"<code>" + esc(resource) + "</code> / <code>" + esc(action) + "</code>" +
|
|
7314
|
+
"</div>"
|
|
7315
|
+
);
|
|
7316
|
+
}
|
|
7317
|
+
|
|
6918
7318
|
function projectRuleRow(r) {
|
|
6919
7319
|
const editing = r.id === ruleEditingId;
|
|
6920
7320
|
const idField = '<div class="field"><span class="k">id</span> <code>' + esc(r.id) + '</code></div>';
|
|
@@ -6946,19 +7346,14 @@ function dashboardHtml() {
|
|
|
6946
7346
|
const description = r.description
|
|
6947
7347
|
? '<div class="label">' + esc(r.description) + '</div>'
|
|
6948
7348
|
: '';
|
|
7349
|
+
const status = ruleStatusField(r.status);
|
|
7350
|
+
const resourceAction = ruleResourceActionField(r);
|
|
6949
7351
|
const matcher =
|
|
6950
7352
|
'<div class="field"><span class="k">matcher</span> <code>' + esc(r.matcher || (r.type === 'bash' ? 'regex' : 'exact')) + '</code></div>';
|
|
6951
|
-
const action =
|
|
6952
|
-
'<div class="field"><span class="k">action</span> <code>' + esc(r.action || '\u2014') + '</code></div>';
|
|
6953
|
-
const resource =
|
|
6954
|
-
'<div class="field"><span class="k">resource</span> <code>' + esc(r.resource || '\u2014') + '</code></div>';
|
|
6955
|
-
const status = r.status
|
|
6956
|
-
? '<div class="field"><span class="k">status</span> <code>' + esc(r.status) + '</code></div>'
|
|
6957
|
-
: '';
|
|
6958
7353
|
return (
|
|
6959
7354
|
'<div class="token-row">' +
|
|
6960
7355
|
'<div class="token-top">' +
|
|
6961
|
-
'<div class="token-info">' + description +
|
|
7356
|
+
'<div class="token-info">' + description + status + resourceAction + typeField + idField + labelField + matcher + '</div>' +
|
|
6962
7357
|
'</div>' +
|
|
6963
7358
|
'<div class="token-actions">' +
|
|
6964
7359
|
'<button type="button" class="btn-edit" data-edit-rule="' + esc(r.id) + '">EDIT</button>' +
|
|
@@ -6993,6 +7388,42 @@ function dashboardHtml() {
|
|
|
6993
7388
|
renderProjectRules(await res.json());
|
|
6994
7389
|
}
|
|
6995
7390
|
|
|
7391
|
+
const guideToastEl = document.getElementById("guide-toast");
|
|
7392
|
+
function showGuideToast(msg, kind) {
|
|
7393
|
+
guideToastEl.textContent = msg;
|
|
7394
|
+
guideToastEl.className = "toast show " + (kind || "success");
|
|
7395
|
+
setTimeout(() => guideToastEl.classList.remove("show"), 4000);
|
|
7396
|
+
}
|
|
7397
|
+
|
|
7398
|
+
async function runPolicyRefresh(btn, showToastFn) {
|
|
7399
|
+
btn.disabled = true;
|
|
7400
|
+
const original = btn.textContent;
|
|
7401
|
+
btn.textContent = "Refreshing\u2026";
|
|
7402
|
+
try {
|
|
7403
|
+
const res = await fetch("/api/policy/refresh", { method: "POST" });
|
|
7404
|
+
const data = await res.json();
|
|
7405
|
+
if (!res.ok) throw new Error(data.error || "Refresh failed");
|
|
7406
|
+
const detail = data.revision != null
|
|
7407
|
+
? " (revision " + data.revision + ", " + data.rules + " rules)"
|
|
7408
|
+
: "";
|
|
7409
|
+
showToastFn(
|
|
7410
|
+
(data.outcome === "not-modified" ? "Policy already current" : "Policy bundle refreshed") + detail,
|
|
7411
|
+
"success");
|
|
7412
|
+
loadProjectRules();
|
|
7413
|
+
} catch (e) {
|
|
7414
|
+
showToastFn(e.message || "Refresh failed", "error");
|
|
7415
|
+
} finally {
|
|
7416
|
+
btn.disabled = false;
|
|
7417
|
+
btn.textContent = original;
|
|
7418
|
+
}
|
|
7419
|
+
}
|
|
7420
|
+
|
|
7421
|
+
const policyRefreshBtn = document.getElementById("policy-refresh");
|
|
7422
|
+
policyRefreshBtn.addEventListener("click", () => runPolicyRefresh(policyRefreshBtn, showRulesToast));
|
|
7423
|
+
document.getElementById("guide-policy-refresh").addEventListener(
|
|
7424
|
+
"click",
|
|
7425
|
+
() => runPolicyRefresh(document.getElementById("guide-policy-refresh"), showGuideToast));
|
|
7426
|
+
|
|
6996
7427
|
function findEditingRule(id) {
|
|
6997
7428
|
return (lastProjectRules.project || []).find((r) => r.id === id);
|
|
6998
7429
|
}
|
|
@@ -7046,10 +7477,9 @@ function dashboardHtml() {
|
|
|
7046
7477
|
? '<div class="tool-badges"><span class="tool-badge rbac-gated" title="Outcome follows your role matrix: 0 block, 1 allow, 2 step-up MFA">Role permission check</span></div>'
|
|
7047
7478
|
: '';
|
|
7048
7479
|
const rbacFields = t.rbacGated
|
|
7049
|
-
? '<div class="field"><span class="k field-k-rbac">
|
|
7050
|
-
esc(t.
|
|
7051
|
-
|
|
7052
|
-
esc(t.rbacResource || 'system') + '</code></div>'
|
|
7480
|
+
? '<div class="field"><span class="k field-k-rbac">Resource / Action</span> : ' +
|
|
7481
|
+
'<code>' + esc(t.rbacResource || 'system') + '</code> / <code>' +
|
|
7482
|
+
esc(t.rbacAction || 'update') + '</code></div>'
|
|
7053
7483
|
: '';
|
|
7054
7484
|
return (
|
|
7055
7485
|
'<div class="token-row">' +
|
|
@@ -7184,6 +7614,37 @@ function listen(port) {
|
|
|
7184
7614
|
sendJson(res, 200, { ok: true, ...buildStatus() });
|
|
7185
7615
|
return;
|
|
7186
7616
|
}
|
|
7617
|
+
if (method === "DELETE" && url === "/api/tokens") {
|
|
7618
|
+
clearTokenFile();
|
|
7619
|
+
sendJson(res, 200, { ok: true, ...buildStatus() });
|
|
7620
|
+
return;
|
|
7621
|
+
}
|
|
7622
|
+
if (method === "POST" && url === "/api/policy/refresh") {
|
|
7623
|
+
let config;
|
|
7624
|
+
try {
|
|
7625
|
+
config = loadStepupConfig();
|
|
7626
|
+
} catch (err) {
|
|
7627
|
+
sendJson(res, 400, {
|
|
7628
|
+
error: err instanceof Error ? err.message : String(err)
|
|
7629
|
+
});
|
|
7630
|
+
return;
|
|
7631
|
+
}
|
|
7632
|
+
const outcome = await refreshPolicyBundle(config, { force: true });
|
|
7633
|
+
if (outcome === "failed") {
|
|
7634
|
+
sendJson(res, 502, {
|
|
7635
|
+
error: "policy bundle refresh failed \u2014 previous cache (if any) kept"
|
|
7636
|
+
});
|
|
7637
|
+
return;
|
|
7638
|
+
}
|
|
7639
|
+
const cached = readCachedPolicyBundle(config.projectId);
|
|
7640
|
+
sendJson(res, 200, {
|
|
7641
|
+
ok: true,
|
|
7642
|
+
outcome,
|
|
7643
|
+
revision: cached?.bundle.revision,
|
|
7644
|
+
rules: cached?.bundle.rules.length
|
|
7645
|
+
});
|
|
7646
|
+
return;
|
|
7647
|
+
}
|
|
7187
7648
|
if (method === "GET" && url === "/api/project-rules") {
|
|
7188
7649
|
sendJson(res, 200, await fetchProjectRulesFromBackend());
|
|
7189
7650
|
return;
|
|
@@ -7344,21 +7805,6 @@ async function runDashboard(options) {
|
|
|
7344
7805
|
}
|
|
7345
7806
|
|
|
7346
7807
|
// src/index.ts
|
|
7347
|
-
var USAGE = `transcodes \u2014 transcodes-guard token manager
|
|
7348
|
-
|
|
7349
|
-
Usage:
|
|
7350
|
-
transcodes Open the dashboard at http://127.0.0.1:3847/ (add --port N or --no-open)
|
|
7351
|
-
transcodes set <token> -l <label> Save your Transcodes member token (label required) to ${transcodesConfigFile()}
|
|
7352
|
-
transcodes reset Remove all saved tokens
|
|
7353
|
-
transcodes status Show the active token source and expiry
|
|
7354
|
-
transcodes tokens List all saved tokens (active one marked with *)
|
|
7355
|
-
transcodes policy refresh Force-refresh the org policy bundle cache now
|
|
7356
|
-
transcodes help Show this message
|
|
7357
|
-
|
|
7358
|
-
The token is read by the transcodes-guard plugins/hooks with precedence:
|
|
7359
|
-
1. TRANSCODES_TOKEN environment variable (overrides everything)
|
|
7360
|
-
2. ${transcodesConfigFile()}
|
|
7361
|
-
`;
|
|
7362
7808
|
function fail(message) {
|
|
7363
7809
|
process.stderr.write(`transcodes: ${message}
|
|
7364
7810
|
`);
|
|
@@ -7532,7 +7978,7 @@ function main() {
|
|
|
7532
7978
|
case "help":
|
|
7533
7979
|
case "--help":
|
|
7534
7980
|
case "-h":
|
|
7535
|
-
process.stdout.write(
|
|
7981
|
+
process.stdout.write(formatCliUsage());
|
|
7536
7982
|
break;
|
|
7537
7983
|
case void 0:
|
|
7538
7984
|
void cmdDashboard([]);
|
package/package.json
CHANGED