@salesforce/sfdx-agent-sdk 0.20.0 → 0.22.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/CHANGELOG.md +15 -0
- package/README.md +12 -11
- package/dist/agent-connectivity-resolver.d.ts +47 -25
- package/dist/agent-connectivity-resolver.js +92 -15
- package/dist/agent-manager.d.ts +17 -1
- package/dist/agent-manager.js +40 -7
- package/dist/agent.d.ts +33 -10
- package/dist/agent.js +38 -50
- package/dist/api-key-connectivity-resolver.d.ts +110 -0
- package/dist/api-key-connectivity-resolver.js +114 -0
- package/dist/errors.d.ts +2 -0
- package/dist/errors.js +2 -0
- package/dist/harness/agent-harness.d.ts +27 -5
- package/dist/harness/harness-bus-owner.d.ts +17 -0
- package/dist/harness/harness-bus-owner.js +27 -0
- package/dist/harness/harness-config.d.ts +3 -2
- package/dist/harness/harness-factory.d.ts +13 -0
- package/dist/harness/stream-input.d.ts +9 -5
- package/dist/harness/stream-input.js +12 -14
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/internal/wire-communication-router.d.ts +43 -0
- package/dist/internal/wire-communication-router.js +119 -0
- package/dist/mcp-auth.d.ts +1 -1
- package/dist/models/claude-opus-4-5.d.ts +11 -0
- package/dist/models/claude-opus-4-5.js +21 -0
- package/dist/models/claude-opus-4-6.d.ts +11 -0
- package/dist/models/claude-opus-4-6.js +22 -0
- package/dist/models/claude-opus-4-7.d.ts +11 -0
- package/dist/models/claude-opus-4-7.js +22 -0
- package/dist/models/claude-sonnet-4-5.d.ts +11 -0
- package/dist/models/claude-sonnet-4-5.js +23 -0
- package/dist/models/claude-sonnet-4-6.d.ts +11 -0
- package/dist/models/claude-sonnet-4-6.js +22 -0
- package/dist/models/create-claude-model.d.ts +54 -0
- package/dist/models/create-claude-model.js +62 -0
- package/dist/models/gpt-5-4.d.ts +11 -0
- package/dist/models/gpt-5-4.js +21 -0
- package/dist/models/gpt-5-5.d.ts +15 -0
- package/dist/models/gpt-5-5.js +24 -0
- package/dist/models/gpt-5.d.ts +11 -0
- package/dist/models/gpt-5.js +23 -0
- package/dist/models/index.d.ts +19 -0
- package/dist/models/index.js +49 -0
- package/dist/models/model.d.ts +69 -0
- package/dist/models/model.js +63 -0
- package/dist/models/multimodal.d.ts +35 -0
- package/dist/models/multimodal.js +78 -0
- package/dist/models/types.d.ts +49 -0
- package/dist/models/types.js +18 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/model-connectivity-info.d.ts +87 -0
- package/dist/types/model-connectivity-info.js +6 -0
- package/dist/types/usage.d.ts +3 -3
- package/dist/types/wire-communication-event.d.ts +124 -0
- package/dist/types/wire-communication-event.js +6 -0
- package/dist/wire-communication-file-writer.d.ts +39 -0
- package/dist/wire-communication-file-writer.js +142 -0
- package/package.json +7 -8
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { UsageMetadata } from './usage.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wire-level communication events, emitted by the harness layer so consumers
|
|
4
|
+
* see the same shape regardless of harness. The union has three members today:
|
|
5
|
+
* - `LlmRequestEvent` (`type: 'llm-request'`) — one outbound HTTP request to
|
|
6
|
+
* the LLM provider.
|
|
7
|
+
* - `LlmResponseEvent` (`type: 'llm-response'`) — the matching inbound
|
|
8
|
+
* response (or terminal failure).
|
|
9
|
+
* - `WireMonitoringNotSupportedEvent` (`type: 'wire-monitoring-not-supported'`)
|
|
10
|
+
* — emitted by harnesses whose runtime doesn't expose a programmatic seam
|
|
11
|
+
* for per-call wire monitoring (e.g. the Claude harness, where the
|
|
12
|
+
* subprocess owns the HTTP traffic). The event carries a free-form
|
|
13
|
+
* `message` explaining the situation and (when applicable) where the
|
|
14
|
+
* underlying data was written instead.
|
|
15
|
+
*
|
|
16
|
+
* **Privacy contract.** Wire-communication events may contain user prompts,
|
|
17
|
+
* tool arguments, model output, and other PII. They are opt-in (subscribed
|
|
18
|
+
* to via `AgentManager.onWireCommunication`), not flowed to log files by
|
|
19
|
+
* default, and intended for diagnostic / debugging use rather than
|
|
20
|
+
* production observability. Consumers must apply their own redaction before
|
|
21
|
+
* persisting these events.
|
|
22
|
+
*
|
|
23
|
+
* **OTel disambiguation.** Despite the related-sounding name, this is NOT a
|
|
24
|
+
* distributed trace (a tree of spans across services produced by
|
|
25
|
+
* OpenTelemetry / Jaeger / Zipkin / Honeycomb). The `xClientTraceId`
|
|
26
|
+
* correlation header is a label for matching gateway-side logs, not a span.
|
|
27
|
+
*
|
|
28
|
+
* **Cross-harness fidelity.** Differs by harness:
|
|
29
|
+
* - Mastra (in-process) emits one `LlmRequestEvent` + `LlmResponseEvent`
|
|
30
|
+
* pair per outbound HTTP call, with the full request body captured.
|
|
31
|
+
* - The Claude harness can't observe per-call HTTP traffic — the Claude
|
|
32
|
+
* Agent SDK runs the model client in a subprocess with no programmatic
|
|
33
|
+
* interception seam. Instead, when subscribers exist at stream-start, the
|
|
34
|
+
* Claude harness writes the subprocess's debug log to a file under the
|
|
35
|
+
* SDK storage root and emits a single `WireMonitoringNotSupportedEvent`
|
|
36
|
+
* per stream, with the file path embedded in `message`.
|
|
37
|
+
*
|
|
38
|
+
* **Subscriber-presence gating.** The wire-communication event chain
|
|
39
|
+
* (`harness.wireBus → router slice → manager.wireBus`) uses
|
|
40
|
+
* `EventBus.forwardWhileSubscribed`, so a harness with zero downstream
|
|
41
|
+
* consumers pays zero cost — the Claude harness in particular skips writing
|
|
42
|
+
* the debug log and skips emitting the `WireMonitoringNotSupportedEvent`
|
|
43
|
+
* when nobody's listening. Consumers must subscribe via
|
|
44
|
+
* `AgentManager.onWireCommunication` BEFORE the `chat()` they want to
|
|
45
|
+
* observe; subscribing mid-stream misses the in-flight subprocess on Claude.
|
|
46
|
+
*/
|
|
47
|
+
export type LlmRequestEvent = {
|
|
48
|
+
/** Direction discriminator. */
|
|
49
|
+
type: 'llm-request';
|
|
50
|
+
/** Wall-clock timestamp when the request was emitted. */
|
|
51
|
+
timestamp: Date;
|
|
52
|
+
/** Fully-qualified URL the harness made the request to. */
|
|
53
|
+
url: string;
|
|
54
|
+
/** HTTP method (`POST`, `GET`, …). */
|
|
55
|
+
method: string;
|
|
56
|
+
/** Native model id sent on the wire (per `ModelConnectivityInfo.nativeModelId`). */
|
|
57
|
+
model: string;
|
|
58
|
+
/** Request body. Best-effort: partial / absent on harnesses that can't observe it. */
|
|
59
|
+
body?: Record<string, unknown>;
|
|
60
|
+
};
|
|
61
|
+
export type LlmResponseEvent = {
|
|
62
|
+
/** Direction discriminator. */
|
|
63
|
+
type: 'llm-response';
|
|
64
|
+
/** Wall-clock timestamp when the response (or terminal failure) was emitted. */
|
|
65
|
+
timestamp: Date;
|
|
66
|
+
/** Native model id from the matching request. */
|
|
67
|
+
model: string;
|
|
68
|
+
/** HTTP status code; `0` for harness-internal terminal failures with no transport response. */
|
|
69
|
+
status: number;
|
|
70
|
+
/** Error captured from a transport / parse failure, if any. */
|
|
71
|
+
error?: Error;
|
|
72
|
+
/**
|
|
73
|
+
* Time from request emit to first byte of response body, in ms.
|
|
74
|
+
*
|
|
75
|
+
* Best-effort and harness-dependent: only populated by harnesses that observe
|
|
76
|
+
* chunk timing on the streaming body. Today the Mastra fetch wrapper emits the
|
|
77
|
+
* `response` event when `globalThis.fetch` resolves (before any chunk has been
|
|
78
|
+
* consumed), so this field is typically `undefined` on Mastra. Consumers should
|
|
79
|
+
* treat the field as advisory and tolerate `undefined`.
|
|
80
|
+
*/
|
|
81
|
+
timeToFirstTokenMs?: number;
|
|
82
|
+
/** Per-call token usage, if reported by the upstream. */
|
|
83
|
+
usage?: UsageMetadata;
|
|
84
|
+
/** Wall-clock duration from request emit to terminal event. */
|
|
85
|
+
totalDurationMs?: number;
|
|
86
|
+
/** Best-effort extracted response text (may be omitted by the harness). */
|
|
87
|
+
responseText?: string;
|
|
88
|
+
/** `x-client-trace-id` header value the SDK / harness attached to the request. */
|
|
89
|
+
xClientTraceId?: string;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Emitted by harnesses whose runtime doesn't expose a programmatic seam for
|
|
93
|
+
* per-call wire monitoring. The event signals to consumers that
|
|
94
|
+
* `LlmRequestEvent` / `LlmResponseEvent` won't be produced for this stream
|
|
95
|
+
* and, in `message`, points at whatever workaround the harness can offer
|
|
96
|
+
* (typically a debug log file path).
|
|
97
|
+
*
|
|
98
|
+
* The Claude harness emits this once per `stream()` call when subscribers
|
|
99
|
+
* exist at stream-start. The Mastra harness never emits it; it has the
|
|
100
|
+
* fetch-wrapper seam and produces structured request/response events
|
|
101
|
+
* directly.
|
|
102
|
+
*/
|
|
103
|
+
export type WireMonitoringNotSupportedEvent = {
|
|
104
|
+
/** Direction discriminator. */
|
|
105
|
+
type: 'wire-monitoring-not-supported';
|
|
106
|
+
/** Wall-clock timestamp when the event was emitted (typically end-of-stream). */
|
|
107
|
+
timestamp: Date;
|
|
108
|
+
/** Identifier of the harness emitting the event (e.g. `'claude'`). */
|
|
109
|
+
harnessId: string;
|
|
110
|
+
/**
|
|
111
|
+
* Free-form, human-readable explanation of why direct wire monitoring
|
|
112
|
+
* isn't available for this stream and where (if anywhere) the underlying
|
|
113
|
+
* data has been written instead. Treat as display text, not parseable
|
|
114
|
+
* structured data — harnesses may include file paths, URLs, or caveats
|
|
115
|
+
* inline. May contain newlines.
|
|
116
|
+
*/
|
|
117
|
+
message: string;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Union over every wire-communication event the harness layer can emit.
|
|
121
|
+
* Discriminate on the `type` field.
|
|
122
|
+
*/
|
|
123
|
+
export type WireCommunicationEvent = LlmRequestEvent | LlmResponseEvent | WireMonitoringNotSupportedEvent;
|
|
124
|
+
export type WireCommunicationEventCallback = (event: WireCommunicationEvent) => void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Unsubscribe } from '@salesforce/agentic-common';
|
|
2
|
+
import type { WireCommunicationEvent } from './types/wire-communication-event.js';
|
|
3
|
+
/**
|
|
4
|
+
* Source the writer subscribes to. Anything exposing `onWireCommunication(callback)`
|
|
5
|
+
* works — typically an {@link AgentManager}, but tests may construct a smaller
|
|
6
|
+
* surface.
|
|
7
|
+
*/
|
|
8
|
+
export type WireCommunicationEmitter = {
|
|
9
|
+
onWireCommunication(callback: (event: WireCommunicationEvent) => void): Unsubscribe;
|
|
10
|
+
};
|
|
11
|
+
export type WireCommunicationFileWriterOptions = {
|
|
12
|
+
/** Absolute (or cwd-relative) path the writer appends Markdown to. */
|
|
13
|
+
filePath: string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional callback invoked when an `appendFile` rejection is observed
|
|
16
|
+
* (disk full, parent directory unwritable, etc.). Without this, write
|
|
17
|
+
* failures are routed to `console.error` so a debugging session can't
|
|
18
|
+
* silently lose events — the writer is itself a debugging aid, so a
|
|
19
|
+
* silently-empty file would be the worst possible failure mode.
|
|
20
|
+
*/
|
|
21
|
+
onError?: (error: Error, event: WireCommunicationEvent) => void;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Subscribes to a {@link WireCommunicationEmitter} (typically an
|
|
25
|
+
* `AgentManager`) and writes each {@link WireCommunicationEvent} to a
|
|
26
|
+
* Markdown file. Used as a debugging aid — consumers can attach one with a
|
|
27
|
+
* single line and inspect the resulting file to see request / response pairs
|
|
28
|
+
* in chronological order.
|
|
29
|
+
*
|
|
30
|
+
* The output may contain user prompts, tool arguments, and model output —
|
|
31
|
+
* apply your own redaction before sharing the file.
|
|
32
|
+
*/
|
|
33
|
+
export declare class WireCommunicationFileWriter {
|
|
34
|
+
private options;
|
|
35
|
+
private unsubscribe;
|
|
36
|
+
constructor(emitter: WireCommunicationEmitter, options?: Partial<WireCommunicationFileWriterOptions>);
|
|
37
|
+
detach(): void;
|
|
38
|
+
private handleEvent;
|
|
39
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026, Salesforce, Inc. All rights reserved.
|
|
3
|
+
* See LICENSE.txt for license terms.
|
|
4
|
+
*/
|
|
5
|
+
import { appendFile } from 'node:fs/promises';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
const DefaultWireCommunicationFileWriterOptions = {
|
|
8
|
+
filePath: resolve(process.cwd(), 'wire-communication.md'),
|
|
9
|
+
};
|
|
10
|
+
function validateOptions(options) {
|
|
11
|
+
if (typeof options.filePath !== 'string' || options.filePath.trim() === '') {
|
|
12
|
+
throw new Error('WireCommunicationFileWriterOptions.filePath must be a non-empty string');
|
|
13
|
+
}
|
|
14
|
+
return options;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Subscribes to a {@link WireCommunicationEmitter} (typically an
|
|
18
|
+
* `AgentManager`) and writes each {@link WireCommunicationEvent} to a
|
|
19
|
+
* Markdown file. Used as a debugging aid — consumers can attach one with a
|
|
20
|
+
* single line and inspect the resulting file to see request / response pairs
|
|
21
|
+
* in chronological order.
|
|
22
|
+
*
|
|
23
|
+
* The output may contain user prompts, tool arguments, and model output —
|
|
24
|
+
* apply your own redaction before sharing the file.
|
|
25
|
+
*/
|
|
26
|
+
export class WireCommunicationFileWriter {
|
|
27
|
+
options;
|
|
28
|
+
unsubscribe;
|
|
29
|
+
constructor(emitter, options = {}) {
|
|
30
|
+
const resolvedOptions = {
|
|
31
|
+
...DefaultWireCommunicationFileWriterOptions,
|
|
32
|
+
...options,
|
|
33
|
+
};
|
|
34
|
+
this.options = validateOptions(resolvedOptions);
|
|
35
|
+
this.unsubscribe = emitter.onWireCommunication((event) => {
|
|
36
|
+
void this.handleEvent(event);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
detach() {
|
|
40
|
+
this.unsubscribe?.();
|
|
41
|
+
this.unsubscribe = undefined;
|
|
42
|
+
}
|
|
43
|
+
async handleEvent(event) {
|
|
44
|
+
const content = formatAsMarkdown(event);
|
|
45
|
+
try {
|
|
46
|
+
await appendFile(this.options.filePath, content);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
50
|
+
if (this.options.onError) {
|
|
51
|
+
try {
|
|
52
|
+
this.options.onError(error, event);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Swallow errors from a misbehaving consumer callback;
|
|
56
|
+
// the writer must not throw out of the event listener.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.error(`[WireCommunicationFileWriter] Failed to append to "${this.options.filePath}":`, error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function formatAsMarkdown(event) {
|
|
66
|
+
switch (event.type) {
|
|
67
|
+
case 'llm-request':
|
|
68
|
+
return formatRequestMarkdown(event);
|
|
69
|
+
case 'llm-response':
|
|
70
|
+
return formatResponseMarkdown(event);
|
|
71
|
+
case 'wire-monitoring-not-supported':
|
|
72
|
+
return formatWireMonitoringNotSupportedMarkdown(event);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function formatRequestMarkdown(event) {
|
|
76
|
+
const dateTime = event.timestamp.toISOString();
|
|
77
|
+
const body = event.body;
|
|
78
|
+
const lines = [
|
|
79
|
+
`### ${dateTime} [${event.type}]`,
|
|
80
|
+
``,
|
|
81
|
+
`**Url**: ${event.method} ${event.url}`,
|
|
82
|
+
``,
|
|
83
|
+
`**Model**: ${event.model}`,
|
|
84
|
+
``,
|
|
85
|
+
];
|
|
86
|
+
if (body !== undefined) {
|
|
87
|
+
lines.push(`**Body**:`, '```json', JSON.stringify(body, null, 2), '```', ``);
|
|
88
|
+
}
|
|
89
|
+
lines.push(``);
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
function formatResponseMarkdown(event) {
|
|
93
|
+
// Optional fields are emitted only when populated. Mastra's fetch wrapper
|
|
94
|
+
// populates `status`, `totalDurationMs`, and (when the gateway returns it)
|
|
95
|
+
// `xClientTraceId`; future harnesses may populate `usage`, `responseText`,
|
|
96
|
+
// and `timeToFirstTokenMs`. Skipping the unset fields keeps the markdown
|
|
97
|
+
// signal-dense rather than padding every section with `N/A` lines.
|
|
98
|
+
const dateTime = event.timestamp.toISOString();
|
|
99
|
+
const lines = [
|
|
100
|
+
`### ${dateTime} [${event.type}]`,
|
|
101
|
+
``,
|
|
102
|
+
`**Model**: ${event.model}`,
|
|
103
|
+
``,
|
|
104
|
+
`**Status**: ${event.status}`,
|
|
105
|
+
``,
|
|
106
|
+
];
|
|
107
|
+
if (event.xClientTraceId !== undefined) {
|
|
108
|
+
lines.push(`**x-client-trace-id**: ${event.xClientTraceId}`, ``);
|
|
109
|
+
}
|
|
110
|
+
if (event.totalDurationMs !== undefined) {
|
|
111
|
+
lines.push(`**Total duration**: ${event.totalDurationMs}`, ``);
|
|
112
|
+
}
|
|
113
|
+
if (event.timeToFirstTokenMs !== undefined) {
|
|
114
|
+
lines.push(`**Time to first token**: ${event.timeToFirstTokenMs}`, ``);
|
|
115
|
+
}
|
|
116
|
+
if (event.error !== undefined) {
|
|
117
|
+
lines.push(`**Error**: ${event.error.message}`, ``);
|
|
118
|
+
}
|
|
119
|
+
if (event.usage !== undefined) {
|
|
120
|
+
if (event.usage.inputTokens !== undefined) {
|
|
121
|
+
lines.push(`**Input tokens**: ${event.usage.inputTokens}`, ``);
|
|
122
|
+
}
|
|
123
|
+
if (event.usage.outputTokens !== undefined) {
|
|
124
|
+
lines.push(`**Output tokens**: ${event.usage.outputTokens}`, ``);
|
|
125
|
+
}
|
|
126
|
+
if (event.usage.totalTokens !== undefined) {
|
|
127
|
+
lines.push(`**Total tokens**: ${event.usage.totalTokens}`, ``);
|
|
128
|
+
}
|
|
129
|
+
if (event.usage.reasoningTokens !== undefined) {
|
|
130
|
+
lines.push(`**Reasoning tokens**: ${event.usage.reasoningTokens}`, ``);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (event.responseText !== undefined) {
|
|
134
|
+
lines.push(`**Response**:`, event.responseText, ``);
|
|
135
|
+
}
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
}
|
|
138
|
+
function formatWireMonitoringNotSupportedMarkdown(event) {
|
|
139
|
+
const dateTime = event.timestamp.toISOString();
|
|
140
|
+
return [`### ${dateTime} [${event.type}]`, ``, `**Harness**: ${event.harnessId}`, ``, event.message, ``, ``].join('\n');
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=wire-communication-file-writer.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/sfdx-agent-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
4
4
|
"description": "Harness-agnostic agentic infrastructure for Salesforce developer experience tooling",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -40,17 +40,16 @@
|
|
|
40
40
|
"LICENSE.txt"
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@salesforce/agentic-common": "0.
|
|
44
|
-
"@salesforce/llm-gateway-sdk": "0.14.0"
|
|
43
|
+
"@salesforce/agentic-common": "0.11.0"
|
|
45
44
|
},
|
|
46
45
|
"devDependencies": {
|
|
47
46
|
"@eslint/js": "^10.0.1",
|
|
48
|
-
"@salesforce/sfdx-agent-harness-claude": "0.
|
|
49
|
-
"@salesforce/sfdx-agent-harness-mastra": "0.
|
|
50
|
-
"@types/node": "^22.19.
|
|
47
|
+
"@salesforce/sfdx-agent-harness-claude": "0.18.0",
|
|
48
|
+
"@salesforce/sfdx-agent-harness-mastra": "0.21.0",
|
|
49
|
+
"@types/node": "^22.19.21",
|
|
51
50
|
"@vitest/coverage-istanbul": "^4.1.8",
|
|
52
|
-
"@vitest/eslint-plugin": "^1.6.
|
|
53
|
-
"eslint": "^10.
|
|
51
|
+
"@vitest/eslint-plugin": "^1.6.20",
|
|
52
|
+
"eslint": "^10.5.0",
|
|
54
53
|
"eslint-config-prettier": "^10.1.8",
|
|
55
54
|
"eslint-import-resolver-typescript": "^4.4.5",
|
|
56
55
|
"eslint-plugin-import": "^2.32.0",
|