@cortexkit/opencode-openai-auth 0.11.1 → 1.0.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 +15 -26
- package/dist/index-766sfrh5.js +1 -0
- package/dist/index-8kj7sa1p.js +12 -0
- package/dist/index-8rqfbhfw.js +221 -0
- package/dist/index-b3q3j39x.js +2 -0
- package/dist/index-bmd7x5aq.js +2 -0
- package/dist/index-eqh53k8v.js +2 -0
- package/dist/{index-sdt2d2ra.js → index-hy9030jh.js} +1 -1
- package/dist/index-jyr8y8k4.js +7 -0
- package/dist/index-rv08jmd4.js +2 -0
- package/dist/index.d.ts +578 -145
- package/dist/index.js +1 -110
- package/dist/pool-lifecycle-DbDmgabd.d.ts +2430 -0
- package/dist/port-file-Do3lQIEk.d.ts +29 -0
- package/dist/protocol-BOBdkqqm.d.ts +198 -0
- package/dist/rpc/port-file.d.ts +4 -17
- package/dist/rpc/port-file.js +1 -1
- package/dist/rpc/protocol.d.ts +2 -2
- package/dist/rpc/protocol.js +1 -0
- package/dist/rpc/rpc-client.d.ts +5 -4
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-dir.d.ts +2 -0
- package/dist/rpc/rpc-dir.js +1 -1
- package/dist/sidebar-state-D6cUKgkm.d.ts +385 -0
- package/dist/sidebar-state.d.ts +2 -2
- package/dist/sidebar-state.js +1 -1
- package/dist/token-util-wdza526f.js +1 -0
- package/dist/{token-9823y0pc.js → token-ypk61xj0.js} +1 -1
- package/dist/tui-preferences.js +5 -2
- package/dist/tui.d.ts +9 -4
- package/dist/v2/server.d.ts +54 -0
- package/dist/v2/server.js +4 -0
- package/package.json +21 -9
- package/src/tui/entry.mjs +26 -25
- package/src/tui-compiled/raw/logger.js +24 -0
- package/src/tui-compiled/raw/rpc/protocol.js +3 -0
- package/src/tui-compiled/raw/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/raw/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/raw/selector.js +21 -0
- package/src/tui-compiled/raw/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/raw/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/raw/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/raw/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/raw/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/raw/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/raw/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/raw/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/raw/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/raw/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/raw/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/raw/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/raw/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/raw/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/raw/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/raw/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/raw/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/raw/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/raw/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/raw/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/raw/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/raw/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/raw/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/raw/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/raw/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/raw/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/raw/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/raw/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/raw/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/raw/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/raw/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/raw/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/raw/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/raw/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/raw/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/raw/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/raw/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/raw/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/raw/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/raw/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/raw/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/raw/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/raw/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/raw/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/raw/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/raw/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/raw/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/raw/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/raw/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/raw/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/raw/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/raw/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/raw/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/raw/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/raw/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/raw/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/raw/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/raw/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/raw/sidebar-state.js +960 -0
- package/src/tui-compiled/raw/tui/command-dialogs.tsx +424 -0
- package/src/tui-compiled/raw/tui-preferences.js +135 -0
- package/src/tui-compiled/raw/tui.tsx +135 -143
- package/src/tui-compiled/runtime/logger.js +24 -0
- package/src/tui-compiled/runtime/rpc/protocol.js +3 -0
- package/src/tui-compiled/runtime/rpc/rpc-client.js +7 -0
- package/src/tui-compiled/runtime/rpc/rpc-dir.js +27 -0
- package/src/tui-compiled/runtime/selector.js +21 -0
- package/src/tui-compiled/runtime/shared/0332701351b3eeec-format.js +138 -0
- package/src/tui-compiled/runtime/shared/04a6357627de5ed0-errors.js +38 -0
- package/src/tui-compiled/runtime/shared/053cc17b5419261e-codec.js +12 -0
- package/src/tui-compiled/runtime/shared/0766d78c6f481f18-watcher.js +157 -0
- package/src/tui-compiled/runtime/shared/08eef45626a94992-reserved.js +13 -0
- package/src/tui-compiled/runtime/shared/39c3cb05738fcf6d-projection.js +134 -0
- package/src/tui-compiled/runtime/shared/3c2d4169d00f907e-tui-support.js +5 -0
- package/src/tui-compiled/runtime/shared/3e7d45a77c6ad4d5-index.js +4 -0
- package/src/tui-compiled/runtime/shared/415792e85d80819e-capture-sink.js +13 -0
- package/src/tui-compiled/runtime/shared/42ae93ab554fcec2-open-url.js +28 -0
- package/src/tui-compiled/runtime/shared/43fc9e6b994519c7-refresh-lock.js +136 -0
- package/src/tui-compiled/runtime/shared/458b934e92490331-mutate.js +500 -0
- package/src/tui-compiled/runtime/shared/45bf5343bbbf9add-admission.js +142 -0
- package/src/tui-compiled/runtime/shared/46f3dff1266160e8-client.js +2 -0
- package/src/tui-compiled/runtime/shared/4825e03209d6ae3e-with-lock.js +48 -0
- package/src/tui-compiled/runtime/shared/4f36d333d081d040-merge.js +162 -0
- package/src/tui-compiled/runtime/shared/54eba94fd64c8c3c-index.js +7 -0
- package/src/tui-compiled/runtime/shared/7211cfec06a298bc-atomic-write.js +80 -0
- package/src/tui-compiled/runtime/shared/754d6017f8e419cf-builtins.js +534 -0
- package/src/tui-compiled/runtime/shared/75bd8c3bd63b315f-identity.js +79 -0
- package/src/tui-compiled/runtime/shared/7a91bb630e6f4804-index.js +1 -0
- package/src/tui-compiled/runtime/shared/826342a4b6a8b89b-model.js +16 -0
- package/src/tui-compiled/runtime/shared/83068c0dbe8a6f13-publication.js +304 -0
- package/src/tui-compiled/runtime/shared/836fc615de6afbd9-settings.js +120 -0
- package/src/tui-compiled/runtime/shared/83bc3ba9d0fe2761-runtime.js +68 -0
- package/src/tui-compiled/runtime/shared/8a18abe4fa2977c8-menu.js +300 -0
- package/src/tui-compiled/runtime/shared/901e149c5a0dbbff-sticky.js +339 -0
- package/src/tui-compiled/runtime/shared/9020c407ce64f672-ordered.js +58 -0
- package/src/tui-compiled/runtime/shared/905bffc0220f43d0-map.js +105 -0
- package/src/tui-compiled/runtime/shared/94820e324f2b4c09-seam.js +301 -0
- package/src/tui-compiled/runtime/shared/97484d9bb98c705b-provider-state.js +226 -0
- package/src/tui-compiled/runtime/shared/9968e3cade6969a1-index.js +2 -0
- package/src/tui-compiled/runtime/shared/9b4da614c45f9cd0-lock-constants.js +14 -0
- package/src/tui-compiled/runtime/shared/9d3fc9e37456504e-refresh.js +225 -0
- package/src/tui-compiled/runtime/shared/9deae3c4da4fd63b-hooks.js +34 -0
- package/src/tui-compiled/runtime/shared/a09b6a533c11858f-tui-preferences.js +77 -0
- package/src/tui-compiled/runtime/shared/a62a27a204266acd-attribution.js +60 -0
- package/src/tui-compiled/runtime/shared/a910277ac43a8626-pull.js +143 -0
- package/src/tui-compiled/runtime/shared/b85c5301fddcb98a-paths.js +5 -0
- package/src/tui-compiled/runtime/shared/c14988621dc60a75-port-file.js +217 -0
- package/src/tui-compiled/runtime/shared/c64d7afbc14ca125-sidebar-file.js +157 -0
- package/src/tui-compiled/runtime/shared/c6b176708c446d6d-pins.js +34 -0
- package/src/tui-compiled/runtime/shared/c7d252bedee01600-pi.js +206 -0
- package/src/tui-compiled/runtime/shared/c852a15cfe489d12-pool.js +120 -0
- package/src/tui-compiled/runtime/shared/cc9e0702a752cea7-index.js +3 -0
- package/src/tui-compiled/runtime/shared/cd94316a888b0a8e-schema.js +754 -0
- package/src/tui-compiled/runtime/shared/d319517e3ea6ad70-redact.js +83 -0
- package/src/tui-compiled/runtime/shared/d5a8f1061ee2f114-index.js +5 -0
- package/src/tui-compiled/runtime/shared/d669d72c40e9e116-logger.js +19 -0
- package/src/tui-compiled/runtime/shared/d7dcba96fea1685c-engine.js +246 -0
- package/src/tui-compiled/runtime/shared/dbef697d1df1cd84-index.js +5 -0
- package/src/tui-compiled/runtime/shared/e4acc4cb713f300c-protocol.js +3 -0
- package/src/tui-compiled/runtime/shared/e8cd4929507508d8-refresh-file-lock.js +501 -0
- package/src/tui-compiled/runtime/shared/ec971142548df90e-index.js +6 -0
- package/src/tui-compiled/runtime/shared/f37fcefc7aedbf25-error.js +11 -0
- package/src/tui-compiled/runtime/shared/fbcdcd0f925e74c0-rpc-client.js +253 -0
- package/src/tui-compiled/runtime/shared/fd45e82f2f9c0a0d-torn.js +294 -0
- package/src/tui-compiled/runtime/shared/fe02abeadc535f77-rows.js +1258 -0
- package/src/tui-compiled/runtime/sidebar-state.js +960 -0
- package/src/tui-compiled/runtime/tui/command-dialogs.js +378 -0
- package/src/tui-compiled/runtime/tui-preferences.js +135 -0
- package/src/tui-compiled/runtime/{tui.tsx → tui.js} +288 -316
- package/dist/index-4effjmr6.js +0 -2
- package/dist/index-9ehhb051.js +0 -2
- package/dist/index-g3hsdjh4.js +0 -2
- package/dist/index-hh52v8sb.js +0 -2
- package/dist/index-jwn4r3sw.js +0 -1
- package/dist/index-sq36b120.js +0 -211
- package/dist/internal-BTndPmW2.d.ts +0 -914
- package/dist/quota-normalize-08hpp51v.js +0 -1
- package/dist/sidebar-state-CoKYKcLn.d.ts +0 -220
- package/dist/token-util-ypgz7rge.js +0 -1
- package/src/tui-compiled/raw/core-account-paths.ts +0 -114
- package/src/tui-compiled/raw/core-custody-state.ts +0 -243
- package/src/tui-compiled/raw/logger.ts +0 -53
- package/src/tui-compiled/raw/rpc-port-file.ts +0 -177
- package/src/tui-compiled/raw/rpc-protocol.ts +0 -12
- package/src/tui-compiled/raw/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/raw/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/raw/sidebar-state.ts +0 -1659
- package/src/tui-compiled/raw/tui-command-dialogs.tsx +0 -1096
- package/src/tui-compiled/raw/tui-preferences.ts +0 -328
- package/src/tui-compiled/runtime/core-account-paths.ts +0 -114
- package/src/tui-compiled/runtime/core-custody-state.ts +0 -243
- package/src/tui-compiled/runtime/logger.ts +0 -53
- package/src/tui-compiled/runtime/rpc-port-file.ts +0 -177
- package/src/tui-compiled/runtime/rpc-protocol.ts +0 -12
- package/src/tui-compiled/runtime/rpc-rpc-client.ts +0 -82
- package/src/tui-compiled/runtime/rpc-rpc-dir.ts +0 -46
- package/src/tui-compiled/runtime/sidebar-state.ts +0 -1659
- package/src/tui-compiled/runtime/tui-command-dialogs.tsx +0 -855
- package/src/tui-compiled/runtime/tui-preferences.ts +0 -328
- package/src/tui-compiled/shared/internal.ts +0 -6
- package/src/tui-compiled/shared/logger.ts +0 -264
- package/src/tui-compiled/shared/paths.ts +0 -33
- package/src/tui-compiled/shared/refresh-file-lock.ts +0 -384
- package/src/tui-compiled/shared/util/error.ts +0 -9
- package/src/tui-compiled/shared/util/open-url.ts +0 -30
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// `ordered` routing: admitted rows tried in a fixed order, moving to the next
|
|
2
|
+
// row only when a response carries one of the caller's retry statuses.
|
|
3
|
+
import { admit, } from "./45bf5343bbbf9add-admission.js";
|
|
4
|
+
/** The former main row's id when the caller supplies none. */
|
|
5
|
+
export const DEFAULT_FORMER_MAIN_ID = 'main';
|
|
6
|
+
/**
|
|
7
|
+
* Resolves a persisted `routing.mode` value. `main-first` and
|
|
8
|
+
* `fallback-first` are aliases of `ordered` that move the former main row;
|
|
9
|
+
* an absent or unrecognised value is `ordered` in roster order. The
|
|
10
|
+
* persisted value is only read here, never rewritten.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveRoutingMode(value) {
|
|
13
|
+
switch (value) {
|
|
14
|
+
case 'sticky-balanced':
|
|
15
|
+
return { mode: 'sticky-balanced', placement: 'roster' };
|
|
16
|
+
case 'main-first':
|
|
17
|
+
return { mode: 'ordered', placement: 'main-first' };
|
|
18
|
+
case 'fallback-first':
|
|
19
|
+
return { mode: 'ordered', placement: 'fallback-first' };
|
|
20
|
+
default:
|
|
21
|
+
return { mode: 'ordered', placement: 'roster' };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Orders `ids` (in roster order) for a placement: `main-first` moves the row
|
|
26
|
+
* named `formerMainId` to the front, `fallback-first` to the back, and
|
|
27
|
+
* `roster` (or a missing former main row) keeps roster order.
|
|
28
|
+
*/
|
|
29
|
+
export function orderForPlacement(ids, placement, formerMainId = DEFAULT_FORMER_MAIN_ID) {
|
|
30
|
+
if (placement === 'roster' || !ids.includes(formerMainId))
|
|
31
|
+
return [...ids];
|
|
32
|
+
const rest = ids.filter((id) => id !== formerMainId);
|
|
33
|
+
return placement === 'main-first'
|
|
34
|
+
? [formerMainId, ...rest]
|
|
35
|
+
: [...rest, formerMainId];
|
|
36
|
+
}
|
|
37
|
+
export function routeOrdered(input) {
|
|
38
|
+
const admission = admit(input);
|
|
39
|
+
const admitted = new Set(admission.admitted.map((row) => row.id));
|
|
40
|
+
const order = orderForPlacement(input.rows.map((row) => row.id), input.placement ?? 'roster', input.formerMainId).filter((id) => admitted.has(id) && input.killswitch?.get(id) !== false);
|
|
41
|
+
return { order, admission };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The next row to try, given the attempts so far: the first row when none
|
|
45
|
+
* has been tried, the next untried row when the last attempt's status is one
|
|
46
|
+
* of `retryStatuses`, and undefined otherwise (the last response stands) or
|
|
47
|
+
* when every row has been tried.
|
|
48
|
+
*/
|
|
49
|
+
export function nextOrderedAttempt(order, attempts, retryStatuses) {
|
|
50
|
+
const last = attempts.at(-1);
|
|
51
|
+
if (last !== undefined) {
|
|
52
|
+
if (last.status === undefined || !retryStatuses.includes(last.status)) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const tried = new Set(attempts.map((attempt) => attempt.id));
|
|
57
|
+
return order.find((id) => !tried.has(id));
|
|
58
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// The per-row quota map: what the pool knows about one row's limits.
|
|
2
|
+
//
|
|
3
|
+
// A row's quota is a set of keyed limits. Each key is a (scope, window label)
|
|
4
|
+
// pair: the scope is `all` for a limit that binds every request, or a model
|
|
5
|
+
// family name for a limit that binds only that family; the label names the
|
|
6
|
+
// window the provider reports (for example `primary` or `secondary`). Each key
|
|
7
|
+
// holds one entry, which is either a reading, a retirement tombstone (the key
|
|
8
|
+
// held a reading until an authoritative observation stopped reporting it), or
|
|
9
|
+
// an absence record (an authoritative observation reported the key as having
|
|
10
|
+
// no limit before any reading was seen). Tombstones and absence records are
|
|
11
|
+
// evidence that the key is known to be unlimited; a missing key is unknown.
|
|
12
|
+
//
|
|
13
|
+
// Beside the limits sits the credit budget, a third pressure axis with its own
|
|
14
|
+
// reset clock. It is a tri-state: absent (never reported), a reading, or
|
|
15
|
+
// cleared (the provider reported that no budget exists).
|
|
16
|
+
//
|
|
17
|
+
// The map is plain JSON so the store can persist it without interpreting it.
|
|
18
|
+
/** The scope of a limit that binds every request regardless of model. */
|
|
19
|
+
export const ALL_SCOPE = 'all';
|
|
20
|
+
/** The window label required when an admission call supplies none. */
|
|
21
|
+
export const DEFAULT_REQUIRED_LABELS = Object.freeze([
|
|
22
|
+
'primary',
|
|
23
|
+
]);
|
|
24
|
+
export function emptyQuotaMap() {
|
|
25
|
+
return { limits: [] };
|
|
26
|
+
}
|
|
27
|
+
/** The time an entry speaks for: its reading, retirement or absence time. */
|
|
28
|
+
export function entryTime(entry) {
|
|
29
|
+
return entry.kind === 'retired' ? entry.retiredAt : entry.checkedAt;
|
|
30
|
+
}
|
|
31
|
+
export function limitKey(scope, label) {
|
|
32
|
+
// JSON-encoding the pair keeps keys unambiguous for any scope or label text.
|
|
33
|
+
return JSON.stringify([scope, label]);
|
|
34
|
+
}
|
|
35
|
+
function isRecord(value) {
|
|
36
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
37
|
+
}
|
|
38
|
+
function isFiniteNumber(value) {
|
|
39
|
+
return typeof value === 'number' && Number.isFinite(value);
|
|
40
|
+
}
|
|
41
|
+
function isName(value) {
|
|
42
|
+
return typeof value === 'string' && value.length > 0;
|
|
43
|
+
}
|
|
44
|
+
function optional(record, key, check) {
|
|
45
|
+
return record[key] === undefined || check(record[key]);
|
|
46
|
+
}
|
|
47
|
+
function isPositiveFinite(value) {
|
|
48
|
+
return isFiniteNumber(value) && value > 0;
|
|
49
|
+
}
|
|
50
|
+
function isString(value) {
|
|
51
|
+
return typeof value === 'string';
|
|
52
|
+
}
|
|
53
|
+
function isQuotaEntry(value) {
|
|
54
|
+
if (!isRecord(value) || !isName(value.scope) || !isName(value.label)) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
switch (value.kind) {
|
|
58
|
+
case 'reading':
|
|
59
|
+
return (isFiniteNumber(value.checkedAt) &&
|
|
60
|
+
isFiniteNumber(value.usedPercent) &&
|
|
61
|
+
optional(value, 'resetsAt', isString) &&
|
|
62
|
+
optional(value, 'windowMinutes', isPositiveFinite));
|
|
63
|
+
case 'retired':
|
|
64
|
+
return isFiniteNumber(value.retiredAt);
|
|
65
|
+
case 'absent':
|
|
66
|
+
return isFiniteNumber(value.checkedAt);
|
|
67
|
+
default:
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function isCreditBudgetEntry(value) {
|
|
72
|
+
if (!isRecord(value) || !isFiniteNumber(value.checkedAt))
|
|
73
|
+
return false;
|
|
74
|
+
if (value.kind === 'cleared')
|
|
75
|
+
return true;
|
|
76
|
+
return (value.kind === 'reading' &&
|
|
77
|
+
typeof value.reached === 'boolean' &&
|
|
78
|
+
optional(value, 'remainingPercent', isFiniteNumber) &&
|
|
79
|
+
optional(value, 'usedPercent', isFiniteNumber) &&
|
|
80
|
+
optional(value, 'resetsAt', isString) &&
|
|
81
|
+
optional(value, 'limit', isFiniteNumber) &&
|
|
82
|
+
optional(value, 'used', isFiniteNumber) &&
|
|
83
|
+
optional(value, 'remaining', isFiniteNumber) &&
|
|
84
|
+
optional(value, 'unit', isString));
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* True when `value` is a well-formed quota map. Unrecognised top-level keys
|
|
88
|
+
* are tolerated (and preserved by merge) so a newer writer's additions
|
|
89
|
+
* survive; a malformed entry, a duplicated (scope, label) key or a malformed
|
|
90
|
+
* budget makes the whole map invalid.
|
|
91
|
+
*/
|
|
92
|
+
export function isQuotaMap(value) {
|
|
93
|
+
if (!isRecord(value) || !Array.isArray(value.limits))
|
|
94
|
+
return false;
|
|
95
|
+
const seen = new Set();
|
|
96
|
+
for (const entry of value.limits) {
|
|
97
|
+
if (!isQuotaEntry(entry))
|
|
98
|
+
return false;
|
|
99
|
+
const key = limitKey(entry.scope, entry.label);
|
|
100
|
+
if (seen.has(key))
|
|
101
|
+
return false;
|
|
102
|
+
seen.add(key);
|
|
103
|
+
}
|
|
104
|
+
return optional(value, 'budget', isCreditBudgetEntry);
|
|
105
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
// The one way out of the process for anything the command menu shows. Every
|
|
2
|
+
// payload a renderer receives (the dialog payload and every apply result) is
|
|
3
|
+
// produced here: each part is projected field by field from its definition,
|
|
4
|
+
// so action bodies and stray properties never travel, and the whole payload
|
|
5
|
+
// is then scrubbed as a backstop: credential-shaped property names are
|
|
6
|
+
// dropped and every string value is passed through the redactor. Nothing
|
|
7
|
+
// outside this module can build a payload without passing through
|
|
8
|
+
// `dialogPayload` or `applyResult`. A failed action never shows its raw
|
|
9
|
+
// exception text: `projectFailure` turns it into a stable code and a message
|
|
10
|
+
// the library or the plugin wrote for the user.
|
|
11
|
+
import { createRedactor } from "./cc9e0702a752cea7-index.js";
|
|
12
|
+
import { PoolOperationError } from "./54eba94fd64c8c3c-index.js";
|
|
13
|
+
/**
|
|
14
|
+
* Secret shapes the seam masks on top of the logger's (bearer tokens, `sk-`
|
|
15
|
+
* keys, JWTs, vault host tokens): a value assigned to a secret-named field
|
|
16
|
+
* (`client_secret=…`, `"api_key": "…"`, `Authorization: …`), and a run of 32
|
|
17
|
+
* or more hex digits, the shape of a minted request secret. A plugin adds its
|
|
18
|
+
* provider's key shapes through `RedactionOptions.extraValuePatterns`.
|
|
19
|
+
*/
|
|
20
|
+
const SEAM_VALUE_PATTERNS = [
|
|
21
|
+
/\b[\w-]*(?:secret|token|password|passwd|api[_-]?key|authorization|verifier)["']?\s*[:=]\s*["']?(?:(?:Bearer|Basic)\s+)?[^\s"'&,;}]+/gi,
|
|
22
|
+
/\b[0-9a-fA-F]{32,}\b/g,
|
|
23
|
+
];
|
|
24
|
+
/** The string redactor every value crossing the seam goes through. */
|
|
25
|
+
export function createTextRedactor(options = {}) {
|
|
26
|
+
const { redactStrings } = createRedactor({
|
|
27
|
+
...options,
|
|
28
|
+
extraValuePatterns: [
|
|
29
|
+
...SEAM_VALUE_PATTERNS,
|
|
30
|
+
...(options.extraValuePatterns ?? []),
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
return (text) => redactStrings(text);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A failure whose message was written for the user. An action throws it to
|
|
37
|
+
* show `message` with the stable `code`; any other thrown value is shown only
|
|
38
|
+
* as a generic message, because its text may quote a request, a response or
|
|
39
|
+
* a credential. `code` is lowercase letters, digits and dashes; anything else
|
|
40
|
+
* is reported as `action-failed`.
|
|
41
|
+
*/
|
|
42
|
+
export class CommandError extends Error {
|
|
43
|
+
code;
|
|
44
|
+
constructor(code, message, options) {
|
|
45
|
+
super(message, options);
|
|
46
|
+
this.name = 'CommandError';
|
|
47
|
+
this.code = code;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const CODE_SHAPE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
51
|
+
/** The code and message shown for an exception the seam cannot vouch for. */
|
|
52
|
+
export const ACTION_FAILED = {
|
|
53
|
+
code: 'action-failed',
|
|
54
|
+
text: 'That action failed.',
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Store failure kinds whose message the store composes itself from row ids
|
|
58
|
+
* and the plugin's own refusal reasons (`protect`). Other kinds can carry a
|
|
59
|
+
* lock or filesystem error's text, or an arbitrary exception from a hook, so
|
|
60
|
+
* they show a generic message with the kind as the code.
|
|
61
|
+
*/
|
|
62
|
+
const STORE_KINDS_WITH_OWN_MESSAGE = new Set([
|
|
63
|
+
'unknown-row',
|
|
64
|
+
'invalid-row',
|
|
65
|
+
'invalid-input',
|
|
66
|
+
'id-exists',
|
|
67
|
+
'id-removed',
|
|
68
|
+
'type-mismatch',
|
|
69
|
+
'no-credential',
|
|
70
|
+
'row-disabled',
|
|
71
|
+
'row-protected',
|
|
72
|
+
'duplicate-identity',
|
|
73
|
+
'row-key-changed',
|
|
74
|
+
'invalid-order',
|
|
75
|
+
'pending-migration',
|
|
76
|
+
'provider',
|
|
77
|
+
'pull',
|
|
78
|
+
'attribution',
|
|
79
|
+
]);
|
|
80
|
+
/** The code and display text for a thrown value; never its raw text. */
|
|
81
|
+
export function projectFailure(error) {
|
|
82
|
+
if (error instanceof CommandError)
|
|
83
|
+
return {
|
|
84
|
+
code: CODE_SHAPE.test(error.code) ? error.code : ACTION_FAILED.code,
|
|
85
|
+
text: error.message,
|
|
86
|
+
};
|
|
87
|
+
if (error instanceof PoolOperationError)
|
|
88
|
+
return {
|
|
89
|
+
code: `pool-${error.kind}`,
|
|
90
|
+
text: STORE_KINDS_WITH_OWN_MESSAGE.has(error.kind)
|
|
91
|
+
? error.message
|
|
92
|
+
: `The account pool could not do that (${error.kind}).`,
|
|
93
|
+
};
|
|
94
|
+
return ACTION_FAILED;
|
|
95
|
+
}
|
|
96
|
+
/** The confirmation shown when an irreversible action names none. */
|
|
97
|
+
export const DEFAULT_IRREVERSIBLE_CONFIRMATION = 'This cannot be undone. Continue?';
|
|
98
|
+
const CREDENTIAL_NAMES = new Set([
|
|
99
|
+
'access',
|
|
100
|
+
'refresh',
|
|
101
|
+
'apikey',
|
|
102
|
+
'password',
|
|
103
|
+
'authheader',
|
|
104
|
+
'credential',
|
|
105
|
+
'credentials',
|
|
106
|
+
]);
|
|
107
|
+
/**
|
|
108
|
+
* True for a property name that looks like it holds a secret. Case, `-` and
|
|
109
|
+
* `_` are ignored, so `api_key`, `API-Key` and `apiKey` all match. Any name
|
|
110
|
+
* ending in `token`, `key` or `secret` matches: no menu field is named that
|
|
111
|
+
* way, so a match is a leak whatever the rest of the name says.
|
|
112
|
+
*/
|
|
113
|
+
function isCredentialName(name) {
|
|
114
|
+
const normalized = name.toLowerCase().replace(/[-_]/g, '');
|
|
115
|
+
return (CREDENTIAL_NAMES.has(normalized) ||
|
|
116
|
+
normalized.endsWith('token') ||
|
|
117
|
+
normalized.endsWith('key') ||
|
|
118
|
+
normalized.endsWith('secret'));
|
|
119
|
+
}
|
|
120
|
+
function isPlainRecord(value) {
|
|
121
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A copy of `value` without credential-shaped properties and with every
|
|
125
|
+
* string redacted; the paths of both go to `report`.
|
|
126
|
+
*/
|
|
127
|
+
function scrub(value, path, report) {
|
|
128
|
+
if (typeof value === 'string') {
|
|
129
|
+
const clean = report.redact(value);
|
|
130
|
+
if (clean !== value)
|
|
131
|
+
report.redacted.push(path);
|
|
132
|
+
return clean;
|
|
133
|
+
}
|
|
134
|
+
if (Array.isArray(value))
|
|
135
|
+
return value.map((entry, index) => scrub(entry, `${path}[${index}]`, report));
|
|
136
|
+
if (!isPlainRecord(value))
|
|
137
|
+
return value;
|
|
138
|
+
const out = {};
|
|
139
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
140
|
+
if (isCredentialName(name)) {
|
|
141
|
+
report.dropped.push(`${path}.${name}`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
Object.defineProperty(out, name, {
|
|
145
|
+
value: scrub(entry, `${path}.${name}`, report),
|
|
146
|
+
enumerable: true,
|
|
147
|
+
writable: true,
|
|
148
|
+
configurable: true,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
function sealed(payload, command, seam) {
|
|
154
|
+
const report = {
|
|
155
|
+
redact: seam.redact,
|
|
156
|
+
dropped: [],
|
|
157
|
+
redacted: [],
|
|
158
|
+
};
|
|
159
|
+
const clean = scrub(payload, 'payload', report);
|
|
160
|
+
if (report.dropped.length > 0)
|
|
161
|
+
seam.logger.warn('credential-shaped field dropped from a command payload', {
|
|
162
|
+
command,
|
|
163
|
+
fields: report.dropped,
|
|
164
|
+
});
|
|
165
|
+
if (report.redacted.length > 0)
|
|
166
|
+
seam.logger.warn('secret-shaped text masked in a command payload', {
|
|
167
|
+
command,
|
|
168
|
+
fields: report.redacted,
|
|
169
|
+
});
|
|
170
|
+
return clean;
|
|
171
|
+
}
|
|
172
|
+
function projectChoice(choice) {
|
|
173
|
+
return { value: String(choice.value), label: String(choice.label) };
|
|
174
|
+
}
|
|
175
|
+
function projectKnob(knob) {
|
|
176
|
+
switch (knob.kind) {
|
|
177
|
+
case 'choice':
|
|
178
|
+
return {
|
|
179
|
+
kind: 'choice',
|
|
180
|
+
id: knob.id,
|
|
181
|
+
label: knob.label,
|
|
182
|
+
choices: (knob.choices ?? []).map(projectChoice),
|
|
183
|
+
...(knob.value !== undefined ? { value: knob.value } : {}),
|
|
184
|
+
};
|
|
185
|
+
case 'toggle':
|
|
186
|
+
return {
|
|
187
|
+
kind: 'toggle',
|
|
188
|
+
id: knob.id,
|
|
189
|
+
label: knob.label,
|
|
190
|
+
value: knob.value === true,
|
|
191
|
+
};
|
|
192
|
+
case 'number':
|
|
193
|
+
return {
|
|
194
|
+
kind: 'number',
|
|
195
|
+
id: knob.id,
|
|
196
|
+
label: knob.label,
|
|
197
|
+
...(knob.value !== undefined ? { value: knob.value } : {}),
|
|
198
|
+
...(knob.min !== undefined ? { min: knob.min } : {}),
|
|
199
|
+
...(knob.max !== undefined ? { max: knob.max } : {}),
|
|
200
|
+
...(knob.required ? { required: true } : {}),
|
|
201
|
+
};
|
|
202
|
+
case 'text':
|
|
203
|
+
// A masked input holds a secret, so its current value is never shown;
|
|
204
|
+
// the action still receives it when the user leaves the input alone.
|
|
205
|
+
return {
|
|
206
|
+
kind: 'text',
|
|
207
|
+
id: knob.id,
|
|
208
|
+
label: knob.label,
|
|
209
|
+
...(knob.value !== undefined && !knob.masked
|
|
210
|
+
? { value: knob.value }
|
|
211
|
+
: {}),
|
|
212
|
+
...(knob.placeholder !== undefined
|
|
213
|
+
? { placeholder: knob.placeholder }
|
|
214
|
+
: {}),
|
|
215
|
+
...(knob.masked ? { masked: true } : {}),
|
|
216
|
+
...(knob.required ? { required: true } : {}),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* The confirmation an action must pass before it runs, or undefined. An
|
|
222
|
+
* irreversible action always has one, even when its definition (built
|
|
223
|
+
* outside the type checker) names none.
|
|
224
|
+
*/
|
|
225
|
+
export function confirmationOf(action) {
|
|
226
|
+
if (action.irreversible === true)
|
|
227
|
+
return {
|
|
228
|
+
message: action.confirm || DEFAULT_IRREVERSIBLE_CONFIRMATION,
|
|
229
|
+
irreversible: true,
|
|
230
|
+
};
|
|
231
|
+
if (action.confirm)
|
|
232
|
+
return { message: action.confirm, irreversible: false };
|
|
233
|
+
return undefined;
|
|
234
|
+
}
|
|
235
|
+
function projectAction(action) {
|
|
236
|
+
const confirm = confirmationOf(action);
|
|
237
|
+
return {
|
|
238
|
+
id: action.id,
|
|
239
|
+
label: action.label,
|
|
240
|
+
...(action.description !== undefined
|
|
241
|
+
? { description: action.description }
|
|
242
|
+
: {}),
|
|
243
|
+
...(action.group !== undefined ? { group: String(action.group) } : {}),
|
|
244
|
+
knobs: (action.knobs ?? []).map(projectKnob),
|
|
245
|
+
...(confirm ? { confirm } : {}),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/** The account fields a renderer may show; see `MenuAccount`. */
|
|
249
|
+
export function projectAccount(account) {
|
|
250
|
+
return {
|
|
251
|
+
id: account.id,
|
|
252
|
+
...(account.label !== undefined ? { label: account.label } : {}),
|
|
253
|
+
enabled: account.enabled,
|
|
254
|
+
type: account.type,
|
|
255
|
+
...(account.identity !== undefined ? { identity: account.identity } : {}),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function projectItem(item) {
|
|
259
|
+
return {
|
|
260
|
+
id: item.id,
|
|
261
|
+
label: item.label,
|
|
262
|
+
...(item.detail !== undefined ? { detail: item.detail } : {}),
|
|
263
|
+
...(item.group !== undefined ? { group: String(item.group) } : {}),
|
|
264
|
+
...(item.status !== undefined ? { status: String(item.status) } : {}),
|
|
265
|
+
...(item.account ? { account: projectAccount(item.account) } : {}),
|
|
266
|
+
...(item.facts !== undefined ? { facts: item.facts } : {}),
|
|
267
|
+
actions: (item.actions ?? []).map(projectAction),
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
function projectSection(section) {
|
|
271
|
+
const { content } = section;
|
|
272
|
+
return {
|
|
273
|
+
id: section.id,
|
|
274
|
+
slot: section.slot,
|
|
275
|
+
title: section.title,
|
|
276
|
+
lines: (content.lines ?? []).map(String),
|
|
277
|
+
items: (content.items ?? []).map(projectItem),
|
|
278
|
+
actions: (content.actions ?? []).map(projectAction),
|
|
279
|
+
...(content.facts !== undefined ? { facts: content.facts } : {}),
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function projectMenu(command, title, sections) {
|
|
283
|
+
return { command, title, sections: sections.map(projectSection) };
|
|
284
|
+
}
|
|
285
|
+
/** The payload a host's TUI receives when the slash command opens. */
|
|
286
|
+
export function dialogPayload(command, title, sections, seam) {
|
|
287
|
+
return sealed({ command, menu: projectMenu(command, title, sections) }, command, seam);
|
|
288
|
+
}
|
|
289
|
+
/** An apply's result: the message and the refreshed menu. */
|
|
290
|
+
export function applyResult(command, title, sections, outcome, seam) {
|
|
291
|
+
return sealed({
|
|
292
|
+
command,
|
|
293
|
+
ok: outcome.ok,
|
|
294
|
+
text: String(outcome.text),
|
|
295
|
+
...(!outcome.ok && outcome.code !== undefined
|
|
296
|
+
? { code: CODE_SHAPE.test(outcome.code) ? outcome.code : 'refused' }
|
|
297
|
+
: {}),
|
|
298
|
+
...(outcome.needsConfirmation ? { needsConfirmation: true } : {}),
|
|
299
|
+
menu: projectMenu(command, title, sections),
|
|
300
|
+
}, command, seam);
|
|
301
|
+
}
|