@mccaskie-labs/workbench-ai 0.3.1 → 0.4.1
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 +12 -0
- package/README.md +35 -0
- package/dist/ai.js +4 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +53 -0
- package/dist/fake.d.ts +3 -0
- package/dist/fake.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/replies-gen.d.ts +20 -0
- package/dist/replies-gen.js +188 -0
- package/dist/replies.d.ts +211 -0
- package/dist/replies.js +482 -0
- package/dist/reply-ui.d.ts +43 -0
- package/dist/reply-ui.js +153 -0
- package/dist/styles.d.ts +33 -0
- package/dist/styles.js +86 -0
- package/dist/types.d.ts +11 -0
- package/package.json +13 -3
package/dist/reply-ui.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// '@mccaskie-labs/workbench-ai/reply-ui': the box a person copies a draft
|
|
2
|
+
// from (docs/capabilities/04-reply-drafter.md). renderReplyBox draws the
|
|
3
|
+
// draft, Copy reply and Write it again; REPLY_SCRIPT (under the page's nonce)
|
|
4
|
+
// makes Copy work with the Clipboard API and, when the browser refuses it,
|
|
5
|
+
// selects the text so the person can copy it themselves. Nothing is ever sent
|
|
6
|
+
// from here: the person pastes the reply.
|
|
7
|
+
//
|
|
8
|
+
// Every element carries a wb-reply-* class (REPLY_STYLES), so a project
|
|
9
|
+
// restyles the box from its own CSS.
|
|
10
|
+
export { REPLY_STYLES } from "./styles.js";
|
|
11
|
+
/** The box's words in English, Tagalog and Taglish. A project's own strings win (`ui.wbreply.<key>`). */
|
|
12
|
+
export const REPLY_WORDS = {
|
|
13
|
+
en: {
|
|
14
|
+
copy: "Copy reply",
|
|
15
|
+
copied: "Copied",
|
|
16
|
+
selected: "Selected: press copy",
|
|
17
|
+
again: "Write it again",
|
|
18
|
+
note_label: "What to change",
|
|
19
|
+
note_hint: "Make it shorter, don't mention the pool…",
|
|
20
|
+
check: "Check this one before you send it:",
|
|
21
|
+
refused: "We can't draft a reply to this one. Please write it yourself.",
|
|
22
|
+
failed: "The draft didn't come through. Try again in a moment.",
|
|
23
|
+
"reason.unknown_fact": "it has a price, date or number that isn't from your details",
|
|
24
|
+
"reason.missing_fact": "something it needed is missing (shown as ___)",
|
|
25
|
+
"reason.too_long": "it's long",
|
|
26
|
+
"reason.ambiguous_incoming": "the message was hard to read",
|
|
27
|
+
"reason.language_unsure": "we weren't sure which language to reply in",
|
|
28
|
+
"reason.never_rule": "it says something your replies never say",
|
|
29
|
+
"reason.contradicts_fact": "it may say the date is free when it isn't",
|
|
30
|
+
},
|
|
31
|
+
tl: {
|
|
32
|
+
copy: "Kopyahin ang sagot",
|
|
33
|
+
copied: "Nakopya na",
|
|
34
|
+
selected: "Napili na: pindutin ang kopya",
|
|
35
|
+
again: "Isulat ulit",
|
|
36
|
+
note_label: "Ano ang babaguhin",
|
|
37
|
+
note_hint: "Paikliin, huwag banggitin ang pool…",
|
|
38
|
+
check: "Tingnan muna ito bago ipadala:",
|
|
39
|
+
refused: "Hindi namin ito masasagot. Pakisulat na lang po ninyo.",
|
|
40
|
+
failed: "Hindi dumating ang sagot. Subukan ulit mamaya.",
|
|
41
|
+
"reason.unknown_fact": "may presyo, petsa o numero na wala sa inyong detalye",
|
|
42
|
+
"reason.missing_fact": "may kulang na detalye (makikita bilang ___)",
|
|
43
|
+
"reason.too_long": "mahaba ito",
|
|
44
|
+
"reason.ambiguous_incoming": "mahirap basahin ang mensahe",
|
|
45
|
+
"reason.language_unsure": "hindi kami sigurado kung anong wika ang gagamitin",
|
|
46
|
+
"reason.never_rule": "may sinabi itong hindi dapat sabihin",
|
|
47
|
+
"reason.contradicts_fact": "baka sinasabi nitong bakante ang petsa kahit hindi",
|
|
48
|
+
},
|
|
49
|
+
taglish: {
|
|
50
|
+
copy: "Copy reply",
|
|
51
|
+
copied: "Copied na",
|
|
52
|
+
selected: "Naka-select na: pindutin ang copy",
|
|
53
|
+
again: "Isulat ulit",
|
|
54
|
+
note_label: "Ano ang babaguhin",
|
|
55
|
+
note_hint: "Paikliin, huwag i-mention ang pool…",
|
|
56
|
+
check: "I-check muna ito bago i-send:",
|
|
57
|
+
refused: "Hindi namin ma-draft ang reply dito. Pakisulat na lang po.",
|
|
58
|
+
failed: "Hindi dumating ang draft. Try ulit mamaya.",
|
|
59
|
+
"reason.unknown_fact": "may price, date o number na wala sa details ninyo",
|
|
60
|
+
"reason.missing_fact": "may kulang na detail (makikita bilang ___)",
|
|
61
|
+
"reason.too_long": "mahaba ito",
|
|
62
|
+
"reason.ambiguous_incoming": "mahirap basahin ang message",
|
|
63
|
+
"reason.language_unsure": "hindi kami sure kung anong language ang gagamitin",
|
|
64
|
+
"reason.never_rule": "may sinabi itong hindi dapat sabihin",
|
|
65
|
+
"reason.contradicts_fact": "baka sinasabi nitong available ang date kahit hindi",
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const esc = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]);
|
|
69
|
+
function wordsFor(o) {
|
|
70
|
+
const own = REPLY_WORDS[o.language ?? "en"];
|
|
71
|
+
return (key) => o.words?.(`ui.wbreply.${key.replace(".", "_")}`) ?? own[key];
|
|
72
|
+
}
|
|
73
|
+
const isDraft = (x) => !!x && typeof x === "object" && typeof x.text === "string" && Array.isArray(x.check_reasons);
|
|
74
|
+
/**
|
|
75
|
+
* The box for a draft, or for why there is none: pass what `ai.reply` gave, or
|
|
76
|
+
* the error it threw. A refusal by the content rules or by Claude shows "We
|
|
77
|
+
* can't draft a reply to this one"; any other error, "Try again".
|
|
78
|
+
*/
|
|
79
|
+
export function renderReplyBox(result, opts = {}) {
|
|
80
|
+
const w = wordsFor(opts);
|
|
81
|
+
if (!isDraft(result)) {
|
|
82
|
+
const code = result?.code;
|
|
83
|
+
const refused = code === "content_refused" || code === "refused";
|
|
84
|
+
return `<section class="wb-reply" data-wb-reply><p class="wb-reply-error" role="alert">${esc(w(refused ? "refused" : "failed"))}</p></section>`;
|
|
85
|
+
}
|
|
86
|
+
const parts = [`<section class="wb-reply" data-wb-reply lang="${result.language === "en" ? "en" : "fil"}">`];
|
|
87
|
+
if (result.needs_human_check && result.check_reasons.length) {
|
|
88
|
+
parts.push(`<p class="wb-reply-check" role="note">${esc(w("check"))} ${esc(result.check_reasons.map((r) => w(`reason.${r}`)).join("; "))}.</p>`);
|
|
89
|
+
}
|
|
90
|
+
const id = `wbreply-${result.workbench.request_id.replace(/[^A-Za-z0-9_-]/g, "")}`;
|
|
91
|
+
parts.push(`<textarea class="wb-reply-text" id="${id}" rows="6" spellcheck="true" aria-label="${esc(w("copy"))}">${esc(result.text)}</textarea>`);
|
|
92
|
+
parts.push(`<div class="wb-reply-actions"><button type="button" class="wb-reply-copy" data-wb-reply-copy aria-controls="${id}">${esc(w("copy"))}</button><span class="wb-reply-status" role="status" aria-live="polite" data-copied="${esc(w("copied"))}" data-selected="${esc(w("selected"))}"></span></div>`);
|
|
93
|
+
if (opts.againAction) {
|
|
94
|
+
const hidden = Object.entries(opts.hidden ?? {})
|
|
95
|
+
.map(([k, v]) => `<input type="hidden" name="${esc(k)}" value="${esc(v)}">`)
|
|
96
|
+
.join("");
|
|
97
|
+
parts.push(`<form class="wb-reply-again" method="post" action="${esc(opts.againAction)}">${hidden}<input type="hidden" name="previous" value="${esc(result.text)}"><input class="wb-reply-note" name="note" maxlength="300" aria-label="${esc(w("note_label"))}" placeholder="${esc(w("note_hint"))}"><button type="submit" class="wb-reply-again-button">${esc(w("again"))}</button></form>`);
|
|
98
|
+
}
|
|
99
|
+
parts.push("</section>");
|
|
100
|
+
return parts.join("");
|
|
101
|
+
}
|
|
102
|
+
/** Copies exactly `text`; when the Clipboard API is missing or refused, selects it instead. */
|
|
103
|
+
export async function copyReply(text, deps) {
|
|
104
|
+
try {
|
|
105
|
+
if (!deps.clipboard)
|
|
106
|
+
throw new Error("no clipboard");
|
|
107
|
+
await deps.clipboard.writeText(text);
|
|
108
|
+
return "copied";
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
deps.select();
|
|
112
|
+
return "selected";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Runs on the page: wires every box's Copy button. Stringified into REPLY_SCRIPT, so it uses nothing from outside. */
|
|
116
|
+
export function mountReplyBoxes(copy) {
|
|
117
|
+
for (const box of Array.from(document.querySelectorAll("[data-wb-reply]"))) {
|
|
118
|
+
const button = box.querySelector("[data-wb-reply-copy]");
|
|
119
|
+
const area = box.querySelector("textarea");
|
|
120
|
+
const status = box.querySelector("[role=status]");
|
|
121
|
+
if (!button || !area || !status)
|
|
122
|
+
continue;
|
|
123
|
+
let timer;
|
|
124
|
+
button.addEventListener("click", () => {
|
|
125
|
+
void copy(area.value, {
|
|
126
|
+
clipboard: navigator.clipboard,
|
|
127
|
+
select: () => {
|
|
128
|
+
area.focus();
|
|
129
|
+
area.select();
|
|
130
|
+
area.setSelectionRange(0, area.value.length);
|
|
131
|
+
},
|
|
132
|
+
}).then((how) => {
|
|
133
|
+
status.textContent = status.dataset[how] ?? "";
|
|
134
|
+
button.classList.toggle("wb-reply-copied", how === "copied");
|
|
135
|
+
if (timer)
|
|
136
|
+
clearTimeout(timer);
|
|
137
|
+
timer = setTimeout(() => {
|
|
138
|
+
status.textContent = "";
|
|
139
|
+
button.classList.remove("wb-reply-copied");
|
|
140
|
+
}, 4000);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The box's script: put it in the page under its nonce, after the boxes.
|
|
147
|
+
*
|
|
148
|
+
* It is the functions' own source, so whatever bundles the app's Worker shapes
|
|
149
|
+
* it. Wrangler's esbuild keeps function names by wrapping them in `__name(…)`,
|
|
150
|
+
* a helper that exists only in the bundle, so the script brings a stand-in of
|
|
151
|
+
* its own: without it the box's buttons do nothing.
|
|
152
|
+
*/
|
|
153
|
+
export const REPLY_SCRIPT = `(function(){var __name=function(f){return f};(${mountReplyBoxes.toString()})(${copyReply.toString()});})();`;
|
package/dist/styles.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** One CSS custom property the rules read, with its default. */
|
|
2
|
+
export interface StyleVariable {
|
|
3
|
+
/** `--wb-<tool>-<what>`. */
|
|
4
|
+
name: string;
|
|
5
|
+
value: string;
|
|
6
|
+
about: string;
|
|
7
|
+
}
|
|
8
|
+
/** A rule for a class in another state, or for an element inside it. */
|
|
9
|
+
export interface StyleState {
|
|
10
|
+
selector: string;
|
|
11
|
+
about: string;
|
|
12
|
+
css: string;
|
|
13
|
+
}
|
|
14
|
+
/** One class the tool puts on an element, with its default rule. */
|
|
15
|
+
export interface StyleClass {
|
|
16
|
+
name: string;
|
|
17
|
+
element: string;
|
|
18
|
+
about: string;
|
|
19
|
+
css: string;
|
|
20
|
+
states?: readonly StyleState[];
|
|
21
|
+
}
|
|
22
|
+
/** Every class and variable the tool draws with (the same shape as workbench-ui's ToolStyles). */
|
|
23
|
+
export interface ToolStyles {
|
|
24
|
+
format: 1;
|
|
25
|
+
tool: string;
|
|
26
|
+
package: string;
|
|
27
|
+
title: string;
|
|
28
|
+
prefix: string;
|
|
29
|
+
variables: readonly StyleVariable[];
|
|
30
|
+
classes: readonly StyleClass[];
|
|
31
|
+
}
|
|
32
|
+
/** The style hooks of the reply box (renderReplyBox, from '@mccaskie-labs/workbench-ai/reply-ui'). */
|
|
33
|
+
export declare const REPLY_STYLES: ToolStyles;
|
package/dist/styles.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// The look the reply box starts with (docs/capabilities-manifest.md §10).
|
|
2
|
+
// Every element renderReplyBox and REPLY_SCRIPT draw carries one of these
|
|
3
|
+
// classes, and every class has a default rule here, so the box looks finished
|
|
4
|
+
// out of the box and a project restyles it from its own CSS by the same names.
|
|
5
|
+
// `npx workbench-ui styles` writes these into the project's
|
|
6
|
+
// public/styles/workbench.css inside `@layer workbench`. The names are public:
|
|
7
|
+
// renaming one is a breaking change.
|
|
8
|
+
const v = (name) => `var(--wb-reply-${name})`;
|
|
9
|
+
const focus = { selector: ":focus-visible", about: "Has focus", css: `outline:none;box-shadow:0 0 0 3px ${v("focus")}` };
|
|
10
|
+
/** The style hooks of the reply box (renderReplyBox, from '@mccaskie-labs/workbench-ai/reply-ui'). */
|
|
11
|
+
export const REPLY_STYLES = {
|
|
12
|
+
format: 1,
|
|
13
|
+
tool: "reply",
|
|
14
|
+
package: "@mccaskie-labs/workbench-ai",
|
|
15
|
+
title: "Reply box",
|
|
16
|
+
prefix: "wb-reply-",
|
|
17
|
+
variables: [
|
|
18
|
+
{ name: "--wb-reply-text", value: "#e8e8ec", about: "Text of the draft, the buttons and the notes" },
|
|
19
|
+
{ name: "--wb-reply-muted", value: "#9a9aa6", about: "Quieter text: the note box's hint, \"Copied\"" },
|
|
20
|
+
{ name: "--wb-reply-surface", value: "#1b1b1f", about: "Background of the box and the draft" },
|
|
21
|
+
{ name: "--wb-reply-line", value: "#33333b", about: "Borders of the box, the draft and the buttons" },
|
|
22
|
+
{ name: "--wb-reply-accent", value: "#4c8dff", about: "The Copy button" },
|
|
23
|
+
{ name: "--wb-reply-on-accent", value: "#ffffff", about: "Text on the Copy button" },
|
|
24
|
+
{ name: "--wb-reply-warn", value: "#d9a441", about: "The edge of the \"check this one\" note" },
|
|
25
|
+
{ name: "--wb-reply-focus", value: "rgba(76,141,255,.4)", about: "The ring round the field or button that has focus" },
|
|
26
|
+
{ name: "--wb-reply-radius", value: "8px", about: "Corner rounding of the box, the draft and the buttons" },
|
|
27
|
+
{ name: "--wb-reply-target", value: "2.75rem", about: "Least height of a button (a 44 px touch target)" },
|
|
28
|
+
],
|
|
29
|
+
classes: [
|
|
30
|
+
{
|
|
31
|
+
name: "wb-reply",
|
|
32
|
+
element: "section",
|
|
33
|
+
about: "The whole box: the draft, Copy and Write it again",
|
|
34
|
+
css: `display:flex;flex-direction:column;gap:.6rem;max-width:36rem;padding:.75rem;color:${v("text")};background:${v("surface")};border:1px solid ${v("line")};border-radius:${v("radius")}`,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "wb-reply-check",
|
|
38
|
+
element: "p",
|
|
39
|
+
about: "\"Check this one before you send it\", and why, when the draft needs a person's eye",
|
|
40
|
+
css: `margin:0;padding:.4rem .6rem;border-left:3px solid ${v("warn")};font-size:.9rem`,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "wb-reply-text",
|
|
44
|
+
element: "textarea",
|
|
45
|
+
about: "The draft, ready to paste (it can be changed before copying)",
|
|
46
|
+
css: `box-sizing:border-box;width:100%;min-height:8rem;margin:0;padding:.6rem;font:inherit;line-height:1.5;color:${v("text")};background:${v("surface")};border:1px solid ${v("line")};border-radius:${v("radius")};resize:vertical`,
|
|
47
|
+
states: [focus],
|
|
48
|
+
},
|
|
49
|
+
{ name: "wb-reply-actions", element: "div", about: "The Copy button and its status", css: "display:flex;flex-wrap:wrap;align-items:center;gap:.6rem" },
|
|
50
|
+
{
|
|
51
|
+
name: "wb-reply-copy",
|
|
52
|
+
element: "button",
|
|
53
|
+
about: "Copy reply",
|
|
54
|
+
css: `min-height:${v("target")};margin:0;padding:0 1.1rem;font:inherit;font-weight:600;color:${v("on-accent")};background:${v("accent")};border:0;border-radius:${v("radius")};cursor:pointer`,
|
|
55
|
+
states: [focus],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "wb-reply-copied",
|
|
59
|
+
element: "button",
|
|
60
|
+
about: "The Copy button just after it copied (a state class, taken off after a few seconds)",
|
|
61
|
+
css: `background:transparent;color:${v("accent")};box-shadow:inset 0 0 0 2px ${v("accent")}`,
|
|
62
|
+
},
|
|
63
|
+
{ name: "wb-reply-status", element: "span", about: "\"Copied\", or \"Selected: press copy\" when the browser won't copy for the page", css: `color:${v("muted")};font-size:.9rem` },
|
|
64
|
+
{
|
|
65
|
+
name: "wb-reply-again",
|
|
66
|
+
element: "form",
|
|
67
|
+
about: "Write it again: a note and its button",
|
|
68
|
+
css: "display:flex;flex-wrap:wrap;gap:.5rem;margin:0",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "wb-reply-note",
|
|
72
|
+
element: "input",
|
|
73
|
+
about: "The note for the next draft (\"make it shorter\")",
|
|
74
|
+
css: `flex:1 1 12rem;min-height:${v("target")};box-sizing:border-box;margin:0;padding:0 .6rem;font:inherit;color:${v("text")};background:${v("surface")};border:1px solid ${v("line")};border-radius:${v("radius")}`,
|
|
75
|
+
states: [focus, { selector: "::placeholder", about: "The hint", css: `color:${v("muted")}` }],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "wb-reply-again-button",
|
|
79
|
+
element: "button",
|
|
80
|
+
about: "Write it again",
|
|
81
|
+
css: `min-height:${v("target")};margin:0;padding:0 1rem;font:inherit;color:${v("text")};background:transparent;border:1px solid ${v("line")};border-radius:${v("radius")};cursor:pointer`,
|
|
82
|
+
states: [focus],
|
|
83
|
+
},
|
|
84
|
+
{ name: "wb-reply-error", element: "p", about: "Why there is no draft: \"We can't draft this one\"", css: "margin:0" },
|
|
85
|
+
],
|
|
86
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
2
|
import type { WorkbenchEnv } from "@mccaskie-labs/workbench-config";
|
|
3
|
+
import type { DraftWithTemplate, ReplyDraft, ReplyGuide, ReplyGuideName, ReplyRequest } from "./replies.ts";
|
|
3
4
|
import type { OwnVoice, SpellingName, VoiceName } from "./writing.ts";
|
|
4
5
|
export type SystemBlock = Anthropic.TextBlockParam;
|
|
5
6
|
export type ContentBlockParam = Anthropic.ContentBlockParam;
|
|
@@ -13,6 +14,8 @@ export interface AiOptions {
|
|
|
13
14
|
/** This install's Gateway address (`WORKBENCH_GATEWAY_URL`). There is no built-in one. */
|
|
14
15
|
url: string;
|
|
15
16
|
fetch?: typeof fetch;
|
|
17
|
+
/** The project's reply guides, `REPLY_GUIDES` from its generated `src/replies.generated.ts`, for `reply`. */
|
|
18
|
+
replyGuides?: Readonly<Record<string, ReplyGuide>>;
|
|
16
19
|
}
|
|
17
20
|
export interface TextRequest {
|
|
18
21
|
/** A Workbench alias (`claude-default`, `claude-best`, `claude-fast`) or an allowlisted model id. */
|
|
@@ -131,6 +134,14 @@ export interface Ai {
|
|
|
131
134
|
budget(): Promise<BudgetStatus>;
|
|
132
135
|
/** Image generation and image search (step 4.8). The key needs the images or search scope. */
|
|
133
136
|
readonly images: AiImages;
|
|
137
|
+
/**
|
|
138
|
+
* A ready-to-paste reply from facts and a reply guide (docs/capabilities/04-reply-drafter.md).
|
|
139
|
+
* The model places each fact as a token; the package fills and checks them, so a price or
|
|
140
|
+
* date the model typed fails the draft (retried once, then `needs_human_check`).
|
|
141
|
+
*/
|
|
142
|
+
reply<G extends ReplyGuideName>(req: ReplyRequest<G>): Promise<ReplyDraft>;
|
|
143
|
+
/** As `reply`, with the token text the draft was filled from (what `replies pick` keeps). */
|
|
144
|
+
replyWithTemplate<G extends ReplyGuideName>(req: ReplyRequest<G>): Promise<DraftWithTemplate>;
|
|
134
145
|
}
|
|
135
146
|
export type AspectRatio = "1:1" | "3:2" | "2:3" | "4:3" | "3:4" | "16:9" | "9:16";
|
|
136
147
|
export interface ImageGenerateRequest {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mccaskie-labs/workbench-ai",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Claude through the Workbench Gateway: text, structured output, streaming and
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Claude through the Workbench Gateway: text, structured output, streaming, budget status and reply drafts whose facts are checked by code, with no Anthropic key in the app. Refusals and truncations throw; createFakeAi for tests.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -18,6 +18,14 @@
|
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"default": "./dist/index.js"
|
|
20
20
|
},
|
|
21
|
+
"./reply-ui": {
|
|
22
|
+
"types": "./dist/reply-ui.d.ts",
|
|
23
|
+
"default": "./dist/reply-ui.js"
|
|
24
|
+
},
|
|
25
|
+
"./styles": {
|
|
26
|
+
"types": "./dist/styles.d.ts",
|
|
27
|
+
"default": "./dist/styles.js"
|
|
28
|
+
},
|
|
21
29
|
"./package.json": "./package.json"
|
|
22
30
|
},
|
|
23
31
|
"bin": {
|
|
@@ -39,13 +47,15 @@
|
|
|
39
47
|
},
|
|
40
48
|
"dependencies": {
|
|
41
49
|
"@anthropic-ai/sdk": "^1.5.0",
|
|
42
|
-
"@mccaskie-labs/workbench-config": "^0.3.0"
|
|
50
|
+
"@mccaskie-labs/workbench-config": "^0.3.0",
|
|
51
|
+
"@mccaskie-labs/workbench-country": "^0.2.1"
|
|
43
52
|
},
|
|
44
53
|
"peerDependencies": {
|
|
45
54
|
"zod": "^4.1.0"
|
|
46
55
|
},
|
|
47
56
|
"devDependencies": {
|
|
48
57
|
"@types/node": "24.19.2",
|
|
58
|
+
"esbuild": "0.28.2",
|
|
49
59
|
"vitest": "^5.1.0",
|
|
50
60
|
"zod": "4.6.7"
|
|
51
61
|
}
|