@genesislcap/ai-assistant 15.23.1-agent-rules.1 → 15.23.1-agent-log-submit.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-assistant.api.json +473 -0
- package/dist/ai-assistant.d.ts +69 -0
- package/dist/custom-elements.json +452 -212
- package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +63 -0
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/utils/resolve-debug-log-note.d.ts +23 -0
- package/dist/dts/utils/resolve-debug-log-note.d.ts.map +1 -0
- package/dist/esm/components/settings-modal/settings-modal.styles.js +65 -5
- package/dist/esm/components/settings-modal/settings-modal.template.js +133 -17
- package/dist/esm/main/main.js +179 -0
- package/dist/esm/utils/resolve-debug-log-note.js +31 -0
- package/package.json +16 -16
package/dist/esm/main/main.js
CHANGED
|
@@ -63,6 +63,7 @@ import { logger } from '../utils/logger';
|
|
|
63
63
|
import { filterVisibleMessages, trailingInteractionRow } from '../utils/message-partition';
|
|
64
64
|
import { CONTEXT_ABORT_MARGIN_TOKENS, resolveContextBudget, } from '../utils/resolve-context-budget';
|
|
65
65
|
import { resolveCostHistoryConfig, } from '../utils/resolve-cost-history-config';
|
|
66
|
+
import { normalizeDebugLogNote, resolveDebugLogNoteConfig, } from '../utils/resolve-debug-log-note';
|
|
66
67
|
import { resolvePreferenceBaseline } from '../utils/resolve-preference-baseline';
|
|
67
68
|
import { stripAgentHandlers } from '../utils/strip-agent-handlers';
|
|
68
69
|
import { addUsage, emptyUsage, sumUsage, totalTokens } from '../utils/sum-usage';
|
|
@@ -112,6 +113,11 @@ const COMPOSER_MENU_MIN_HEIGHT = 140;
|
|
|
112
113
|
* assistant is running — the Usage tab renders the in-memory list — so the delay is invisible.
|
|
113
114
|
*/
|
|
114
115
|
const LEDGER_FLUSH_DEBOUNCE_MS = 2000;
|
|
116
|
+
/**
|
|
117
|
+
* How long the agent-log Send button shows its 'Sent'/'Failed' outcome before
|
|
118
|
+
* returning to idle — long enough to read, short enough that a retry is never blocked.
|
|
119
|
+
*/
|
|
120
|
+
const DEBUG_LOG_SUBMIT_RESULT_FLASH_MS = 4000;
|
|
115
121
|
/**
|
|
116
122
|
* The image types BOTH vendors accept, and therefore the only ones sent as image blocks.
|
|
117
123
|
*
|
|
@@ -384,6 +390,25 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
384
390
|
* element has its own settings modal.
|
|
385
391
|
*/
|
|
386
392
|
this.downloadingDebugLog = false;
|
|
393
|
+
/**
|
|
394
|
+
* True while `submitDebugLog` is building and handing off the log. Same role as
|
|
395
|
+
* `downloadingDebugLog` for the Send button: the lifetime-stitch and the host's
|
|
396
|
+
* `send` callback are async, so the button needs a visible in-flight state.
|
|
397
|
+
*/
|
|
398
|
+
this.submittingDebugLog = false;
|
|
399
|
+
/**
|
|
400
|
+
* Outcome of the last log submission, shown briefly on the button ('Sent' /
|
|
401
|
+
* 'Failed — retry') then cleared by `setDebugLogSubmitResult`'s timer. `null` when idle.
|
|
402
|
+
*/
|
|
403
|
+
this.debugLogSubmitResult = null;
|
|
404
|
+
/**
|
|
405
|
+
* Whether the agent-log compose box (note + Send/Cancel) is open. Opened by the row's
|
|
406
|
+
* Send button when `debugLogSubmit.note` is not `false`; closed by Cancel or a
|
|
407
|
+
* successful send. Stays open after a failure so the typed note is not lost.
|
|
408
|
+
*/
|
|
409
|
+
this.debugLogComposeOpen = false;
|
|
410
|
+
/** The note typed in the compose box, raw (trimmed only at submit time). */
|
|
411
|
+
this.debugLogNote = '';
|
|
387
412
|
/** Active tab inside the settings modal. */
|
|
388
413
|
this.settingsModalTab = 'settings';
|
|
389
414
|
/**
|
|
@@ -2986,6 +3011,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
2986
3011
|
super.disconnectedCallback();
|
|
2987
3012
|
this._sessionActive = false;
|
|
2988
3013
|
this.stopLoadingTimer();
|
|
3014
|
+
// The 'Sent'/'Failed' flash timer would otherwise fire against a torn-down element.
|
|
3015
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
3016
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
2989
3017
|
this.state = 'idle';
|
|
2990
3018
|
// Note: `compacting`/`restoring` are NOT cleared here. They're written through the
|
|
2991
3019
|
// in-flight op's captured session store (see `initPersistenceForNewStore` /
|
|
@@ -4348,6 +4376,42 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
4348
4376
|
void this.chatConfig;
|
|
4349
4377
|
return this.resolvedCostHistory.showTokens;
|
|
4350
4378
|
}
|
|
4379
|
+
/**
|
|
4380
|
+
* Whether the settings "Agent log" section renders — shown when either action is
|
|
4381
|
+
* enabled: the download button (`allowDebugDownload`) or the host-transport Send
|
|
4382
|
+
* button (`debugLogSubmit`). Each button is additionally gated on its own flag
|
|
4383
|
+
* inside the section, so a host can enable one without the other.
|
|
4384
|
+
*/
|
|
4385
|
+
get settingsAgentLogSectionVisible() {
|
|
4386
|
+
void this.chatConfig;
|
|
4387
|
+
const ui = this.chatConfig.ui;
|
|
4388
|
+
return (ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true || (ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null;
|
|
4389
|
+
}
|
|
4390
|
+
/**
|
|
4391
|
+
* The resolved compose-box settings for the agent-log Send button, or `null` when the
|
|
4392
|
+
* host opted out (`debugLogSubmit.note: false`) and the button sends on first click.
|
|
4393
|
+
*/
|
|
4394
|
+
get debugLogNoteConfig() {
|
|
4395
|
+
var _a;
|
|
4396
|
+
void this.chatConfig;
|
|
4397
|
+
return resolveDebugLogNoteConfig((_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit);
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Whether the compose box's Send button is inert: a send is already in flight, or the
|
|
4401
|
+
* host made the note required and it is still blank.
|
|
4402
|
+
*/
|
|
4403
|
+
get debugLogSendDisabled() {
|
|
4404
|
+
if (this.submittingDebugLog)
|
|
4405
|
+
return true;
|
|
4406
|
+
const note = this.debugLogNoteConfig;
|
|
4407
|
+
return (note === null || note === void 0 ? void 0 : note.required) === true && normalizeDebugLogNote(this.debugLogNote) === undefined;
|
|
4408
|
+
}
|
|
4409
|
+
/** Live "typed / max" counter for the compose box. */
|
|
4410
|
+
get debugLogNoteCounter() {
|
|
4411
|
+
var _a;
|
|
4412
|
+
const max = (_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.maxLength;
|
|
4413
|
+
return max ? `${this.debugLogNote.length}/${max}` : '';
|
|
4414
|
+
}
|
|
4351
4415
|
/** Whether the chat header (and settings cog) should render. */
|
|
4352
4416
|
get settingsCogVisible() {
|
|
4353
4417
|
void this.chatConfig;
|
|
@@ -4360,6 +4424,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
4360
4424
|
(ui === null || ui === void 0 ? void 0 : ui.showNarration) != null ||
|
|
4361
4425
|
(ui === null || ui === void 0 ? void 0 : ui.showAgentSwitchIndicator) != null ||
|
|
4362
4426
|
(ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true ||
|
|
4427
|
+
(ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null ||
|
|
4363
4428
|
(ui === null || ui === void 0 ? void 0 : ui.animations) != null ||
|
|
4364
4429
|
cost.showCost ||
|
|
4365
4430
|
cost.showTokens ||
|
|
@@ -4742,6 +4807,96 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
4742
4807
|
}
|
|
4743
4808
|
});
|
|
4744
4809
|
}
|
|
4810
|
+
/**
|
|
4811
|
+
* Hand the agent log to the host's `ui.debugLogSubmit.send` callback — the Send
|
|
4812
|
+
* button next to Download. Reuses `buildDownloadLog`, so the submitted log is
|
|
4813
|
+
* byte-identical to what Download saves (lifetime-stitched when the provider
|
|
4814
|
+
* persists diagnostics). The assistant is vendor-agnostic: the callback owns the
|
|
4815
|
+
* transport (Sentry feedback, a support API, an upload endpoint). Outcome lands in
|
|
4816
|
+
* `debugLogSubmitResult` so the button can flash 'Sent'/'Failed'; failures also go
|
|
4817
|
+
* to the logger with the cause.
|
|
4818
|
+
*/
|
|
4819
|
+
submitDebugLog() {
|
|
4820
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4821
|
+
var _a, _b, _c, _d;
|
|
4822
|
+
if (this.submittingDebugLog || this.debugLogSendDisabled)
|
|
4823
|
+
return;
|
|
4824
|
+
const config = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit;
|
|
4825
|
+
if (!config)
|
|
4826
|
+
return;
|
|
4827
|
+
const agentName = (_d = (_c = (_b = this.activeAgent) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : this.headerTitle) !== null && _d !== void 0 ? _d : 'chat';
|
|
4828
|
+
const timestamp = new Date().toISOString().replace(/:/g, '-');
|
|
4829
|
+
const fileStem = agentName.toLowerCase().replace(/\s+/g, '-');
|
|
4830
|
+
// Only a note the user could actually type travels with the log: with the compose box
|
|
4831
|
+
// opted out there is no note, whatever a stale `debugLogNote` might hold.
|
|
4832
|
+
const message = this.debugLogNoteConfig === null ? undefined : normalizeDebugLogNote(this.debugLogNote);
|
|
4833
|
+
this.submittingDebugLog = true;
|
|
4834
|
+
try {
|
|
4835
|
+
const payload = yield this.buildDownloadLog();
|
|
4836
|
+
yield config.send({
|
|
4837
|
+
logJson: JSON.stringify(payload, null, 2),
|
|
4838
|
+
filename: `${fileStem}-${timestamp}.json`,
|
|
4839
|
+
agentName,
|
|
4840
|
+
host: window.location.host,
|
|
4841
|
+
message,
|
|
4842
|
+
});
|
|
4843
|
+
// Success consumes the note; a failure (below) keeps the box open with it intact.
|
|
4844
|
+
this.debugLogComposeOpen = false;
|
|
4845
|
+
this.debugLogNote = '';
|
|
4846
|
+
this.setDebugLogSubmitResult('sent');
|
|
4847
|
+
}
|
|
4848
|
+
catch (e) {
|
|
4849
|
+
logger.error('Submitting agent log failed:', e);
|
|
4850
|
+
this.setDebugLogSubmitResult('error');
|
|
4851
|
+
}
|
|
4852
|
+
finally {
|
|
4853
|
+
this.submittingDebugLog = false;
|
|
4854
|
+
}
|
|
4855
|
+
});
|
|
4856
|
+
}
|
|
4857
|
+
/**
|
|
4858
|
+
* The row's Send button. With a compose box configured (the default) it opens the box
|
|
4859
|
+
* and focuses the note so the user can caption the log; with `note: false` it sends
|
|
4860
|
+
* straight away, as before.
|
|
4861
|
+
*/
|
|
4862
|
+
openDebugLogCompose() {
|
|
4863
|
+
if (this.submittingDebugLog)
|
|
4864
|
+
return;
|
|
4865
|
+
if (this.debugLogNoteConfig === null) {
|
|
4866
|
+
void this.submitDebugLog();
|
|
4867
|
+
return;
|
|
4868
|
+
}
|
|
4869
|
+
// Opening again resets a lingering 'Failed' flash so the box starts clean.
|
|
4870
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
4871
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
4872
|
+
this.debugLogSubmitResult = null;
|
|
4873
|
+
this.debugLogComposeOpen = true;
|
|
4874
|
+
// Next frame so the `when` block has rendered the text-area. The design-system
|
|
4875
|
+
// text-area does not delegate focus (see `maybeAutoFocusChatInput`), so drill in.
|
|
4876
|
+
requestAnimationFrame(() => {
|
|
4877
|
+
var _a, _b, _c;
|
|
4878
|
+
if (!this.debugLogComposeOpen || !this.isConnected)
|
|
4879
|
+
return;
|
|
4880
|
+
const el = this.debugLogNoteEl;
|
|
4881
|
+
(_c = ((_b = (_a = el === null || el === void 0 ? void 0 : el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('textarea')) !== null && _b !== void 0 ? _b : el)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
4882
|
+
});
|
|
4883
|
+
}
|
|
4884
|
+
/** Close the compose box and discard the typed note. */
|
|
4885
|
+
cancelDebugLogCompose() {
|
|
4886
|
+
if (this.submittingDebugLog)
|
|
4887
|
+
return;
|
|
4888
|
+
this.debugLogComposeOpen = false;
|
|
4889
|
+
this.debugLogNote = '';
|
|
4890
|
+
}
|
|
4891
|
+
/** Flash the submit outcome on the Send button, then fall back to idle. */
|
|
4892
|
+
setDebugLogSubmitResult(result) {
|
|
4893
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
4894
|
+
this.debugLogSubmitResult = result;
|
|
4895
|
+
this.debugLogSubmitResultTimer = setTimeout(() => {
|
|
4896
|
+
this.debugLogSubmitResult = null;
|
|
4897
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
4898
|
+
}, DEBUG_LOG_SUBMIT_RESULT_FLASH_MS);
|
|
4899
|
+
}
|
|
4745
4900
|
/**
|
|
4746
4901
|
* The log to download (GENC-1351 §5.8). When the provider persists diagnostics,
|
|
4747
4902
|
* stitch the **whole lifetime** stream (every page load) rather than just the
|
|
@@ -5597,6 +5752,18 @@ __decorate([
|
|
|
5597
5752
|
__decorate([
|
|
5598
5753
|
observable
|
|
5599
5754
|
], FoundationAiAssistant.prototype, "downloadingDebugLog", void 0);
|
|
5755
|
+
__decorate([
|
|
5756
|
+
observable
|
|
5757
|
+
], FoundationAiAssistant.prototype, "submittingDebugLog", void 0);
|
|
5758
|
+
__decorate([
|
|
5759
|
+
observable
|
|
5760
|
+
], FoundationAiAssistant.prototype, "debugLogSubmitResult", void 0);
|
|
5761
|
+
__decorate([
|
|
5762
|
+
observable
|
|
5763
|
+
], FoundationAiAssistant.prototype, "debugLogComposeOpen", void 0);
|
|
5764
|
+
__decorate([
|
|
5765
|
+
observable
|
|
5766
|
+
], FoundationAiAssistant.prototype, "debugLogNote", void 0);
|
|
5600
5767
|
__decorate([
|
|
5601
5768
|
observable
|
|
5602
5769
|
], FoundationAiAssistant.prototype, "settingsModalTab", void 0);
|
|
@@ -5744,6 +5911,18 @@ __decorate([
|
|
|
5744
5911
|
__decorate([
|
|
5745
5912
|
volatile
|
|
5746
5913
|
], FoundationAiAssistant.prototype, "settingsSessionTokensVisible", null);
|
|
5914
|
+
__decorate([
|
|
5915
|
+
volatile
|
|
5916
|
+
], FoundationAiAssistant.prototype, "settingsAgentLogSectionVisible", null);
|
|
5917
|
+
__decorate([
|
|
5918
|
+
volatile
|
|
5919
|
+
], FoundationAiAssistant.prototype, "debugLogNoteConfig", null);
|
|
5920
|
+
__decorate([
|
|
5921
|
+
volatile
|
|
5922
|
+
], FoundationAiAssistant.prototype, "debugLogSendDisabled", null);
|
|
5923
|
+
__decorate([
|
|
5924
|
+
volatile
|
|
5925
|
+
], FoundationAiAssistant.prototype, "debugLogNoteCounter", null);
|
|
5747
5926
|
__decorate([
|
|
5748
5927
|
volatile
|
|
5749
5928
|
], FoundationAiAssistant.prototype, "settingsCogVisible", null);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Default cap on the user's note. Short by design — it is a caption for the log, not a report. */
|
|
2
|
+
export const DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH = 500;
|
|
3
|
+
/** Default prompt in the empty note. */
|
|
4
|
+
export const DEBUG_LOG_NOTE_DEFAULT_PLACEHOLDER = 'What were you doing, or what went wrong? (optional)';
|
|
5
|
+
/**
|
|
6
|
+
* Resolve `debugLogSubmit.note` — `undefined`/`true` mean "compose box with defaults",
|
|
7
|
+
* `false` means "no compose box, send on first click" (→ `null`), and an object customises
|
|
8
|
+
* the box. Keeps the template free of the boolean-or-object dance.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveDebugLogNoteConfig(config) {
|
|
11
|
+
var _a;
|
|
12
|
+
if (!config || config.note === false)
|
|
13
|
+
return null;
|
|
14
|
+
const note = typeof config.note === 'object' ? config.note : {};
|
|
15
|
+
const maxLength = typeof note.maxLength === 'number' && note.maxLength > 0
|
|
16
|
+
? Math.floor(note.maxLength)
|
|
17
|
+
: DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH;
|
|
18
|
+
return {
|
|
19
|
+
placeholder: (_a = note.placeholder) !== null && _a !== void 0 ? _a : DEBUG_LOG_NOTE_DEFAULT_PLACEHOLDER,
|
|
20
|
+
required: note.required === true,
|
|
21
|
+
maxLength,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Normalise the typed note for the submission: trimmed, and `undefined` when empty so a
|
|
26
|
+
* host can `message ?? fallback` without checking for whitespace.
|
|
27
|
+
*/
|
|
28
|
+
export function normalizeDebugLogNote(note) {
|
|
29
|
+
const trimmed = note.trim();
|
|
30
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/ai-assistant",
|
|
3
3
|
"description": "Genesis AI Assistant micro-frontend",
|
|
4
|
-
"version": "15.23.1-agent-
|
|
4
|
+
"version": "15.23.1-agent-log-submit.2",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -74,26 +74,26 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@genesislcap/foundation-testing": "15.23.1-agent-
|
|
78
|
-
"@genesislcap/genx": "15.23.1-agent-
|
|
79
|
-
"@genesislcap/rollup-builder": "15.23.1-agent-
|
|
80
|
-
"@genesislcap/ts-builder": "15.23.1-agent-
|
|
81
|
-
"@genesislcap/uvu-playwright-builder": "15.23.1-agent-
|
|
82
|
-
"@genesislcap/vite-builder": "15.23.1-agent-
|
|
83
|
-
"@genesislcap/webpack-builder": "15.23.1-agent-
|
|
77
|
+
"@genesislcap/foundation-testing": "15.23.1-agent-log-submit.2",
|
|
78
|
+
"@genesislcap/genx": "15.23.1-agent-log-submit.2",
|
|
79
|
+
"@genesislcap/rollup-builder": "15.23.1-agent-log-submit.2",
|
|
80
|
+
"@genesislcap/ts-builder": "15.23.1-agent-log-submit.2",
|
|
81
|
+
"@genesislcap/uvu-playwright-builder": "15.23.1-agent-log-submit.2",
|
|
82
|
+
"@genesislcap/vite-builder": "15.23.1-agent-log-submit.2",
|
|
83
|
+
"@genesislcap/webpack-builder": "15.23.1-agent-log-submit.2",
|
|
84
84
|
"@types/dompurify": "^3.0.5",
|
|
85
85
|
"@types/marked": "^5.0.2",
|
|
86
86
|
"esbuild": "0.25.12"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@genesislcap/foundation-ai": "15.23.1-agent-
|
|
90
|
-
"@genesislcap/foundation-logger": "15.23.1-agent-
|
|
91
|
-
"@genesislcap/foundation-notifications": "15.23.1-agent-
|
|
92
|
-
"@genesislcap/foundation-redux": "15.23.1-agent-
|
|
93
|
-
"@genesislcap/foundation-ui": "15.23.1-agent-
|
|
94
|
-
"@genesislcap/foundation-utils": "15.23.1-agent-
|
|
95
|
-
"@genesislcap/rapid-design-system": "15.23.1-agent-
|
|
96
|
-
"@genesislcap/web-core": "15.23.1-agent-
|
|
89
|
+
"@genesislcap/foundation-ai": "15.23.1-agent-log-submit.2",
|
|
90
|
+
"@genesislcap/foundation-logger": "15.23.1-agent-log-submit.2",
|
|
91
|
+
"@genesislcap/foundation-notifications": "15.23.1-agent-log-submit.2",
|
|
92
|
+
"@genesislcap/foundation-redux": "15.23.1-agent-log-submit.2",
|
|
93
|
+
"@genesislcap/foundation-ui": "15.23.1-agent-log-submit.2",
|
|
94
|
+
"@genesislcap/foundation-utils": "15.23.1-agent-log-submit.2",
|
|
95
|
+
"@genesislcap/rapid-design-system": "15.23.1-agent-log-submit.2",
|
|
96
|
+
"@genesislcap/web-core": "15.23.1-agent-log-submit.2",
|
|
97
97
|
"dompurify": "^3.3.1",
|
|
98
98
|
"marked": "^17.0.3"
|
|
99
99
|
},
|