@lenne.tech/nuxt-extensions 1.21.0 → 1.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/CLAUDE.md +1 -0
- package/README.md +41 -0
- package/dist/module.d.mts +1 -1
- package/dist/module.json +1 -1
- package/dist/module.mjs +2 -1
- package/dist/runtime/composables/index.d.ts +1 -0
- package/dist/runtime/composables/index.js +1 -0
- package/dist/runtime/composables/use-lt-ai-settings.d.ts +12 -0
- package/dist/runtime/composables/use-lt-ai-settings.js +32 -0
- package/dist/runtime/types/ai.d.ts +21 -0
- package/dist/runtime/types/index.d.ts +1 -1
- package/package.json +5 -5
package/CLAUDE.md
CHANGED
|
@@ -40,6 +40,7 @@ src/
|
|
|
40
40
|
| `useLtAi()` | One-shot `prompt()` + streaming `promptStream()` (POST `/ai/prompt` / `/ai/stream`) |
|
|
41
41
|
| `useLtAiChat()` | Multi-turn chat: streaming, budget summary, confirmation gate, `stop()`/`clear()`, optional `maxMessages` cap, auto-stop on unmount |
|
|
42
42
|
| `useLtAiConnections()` | User-facing list of available connections + `select()` |
|
|
43
|
+
| `useLtAiSettings()` | The user's own AI settings: MCP confirmation switch (`GET/PUT /ai/settings`, `enforced` → show disabled) |
|
|
43
44
|
| `useLtAiUsage()` | Full token/prompt usage breakdown (`GET /ai/usage`) |
|
|
44
45
|
| `useLtAiPrompts()` | User-facing CRUD for re-usable prompt snippets ("Vorlagen", `scope: 'user'` / `'tenant'`) |
|
|
45
46
|
| `useLtAiPlaceholders()` | Loads the backend's `{{placeholder}}` registry for editor sidebars |
|
package/README.md
CHANGED
|
@@ -709,6 +709,45 @@ await load();
|
|
|
709
709
|
is private to the owner; `scope: 'tenant'` is shared across the owner's tenant. The list
|
|
710
710
|
returned by `load()` is already server-filtered to what the user is allowed to see.
|
|
711
711
|
|
|
712
|
+
### MCP confirmation switch
|
|
713
|
+
|
|
714
|
+
Over MCP (Claude Desktop, Claude Code and other clients), nest-server 12.3.0 or later asks the
|
|
715
|
+
user before a destructive tool runs, and before a mutating one if the server is configured that
|
|
716
|
+
way. Each user may switch the question off for themselves, like a "bypass permissions" mode.
|
|
717
|
+
`useLtAiSettings()` reads and changes that switch (`GET/PUT /ai/settings`):
|
|
718
|
+
|
|
719
|
+
```vue
|
|
720
|
+
<script setup lang="ts">
|
|
721
|
+
const { enforced, load, settings, update } = useLtAiSettings();
|
|
722
|
+
await load();
|
|
723
|
+
|
|
724
|
+
async function toggle(value: boolean) {
|
|
725
|
+
try {
|
|
726
|
+
await update({ mcpConfirmation: value });
|
|
727
|
+
} catch {
|
|
728
|
+
// `error` holds the message; `settings` keeps the previous value, so the switch snaps back
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
</script>
|
|
732
|
+
|
|
733
|
+
<template>
|
|
734
|
+
<USwitch
|
|
735
|
+
:model-value="settings?.mcpConfirmation"
|
|
736
|
+
:disabled="enforced || !settings"
|
|
737
|
+
label="Ask before destructive AI actions over MCP"
|
|
738
|
+
@update:model-value="toggle"
|
|
739
|
+
/>
|
|
740
|
+
</template>
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
- `settings` is always the value as it takes effect, never just the stored choice. `update()`
|
|
744
|
+
adopts the server's answer instead of the input.
|
|
745
|
+
- `enforced` is true when the server fixed the value (`ai.mcp.confirmation.enforced`). Show the
|
|
746
|
+
switch disabled. A change is refused with 403 `#LTNS_0609`, which `update()` throws.
|
|
747
|
+
- Switching it off lets an MCP client run destructive tools in this user's name without asking.
|
|
748
|
+
Say so next to the switch. The tool's own `authorize()` check and the admin's `deny` / `ask`
|
|
749
|
+
rules still apply, and other users and the in-app chat are not affected.
|
|
750
|
+
|
|
712
751
|
### Lower-level + supporting composables
|
|
713
752
|
|
|
714
753
|
| Composable | Purpose |
|
|
@@ -716,6 +755,7 @@ returned by `load()` is already server-filtered to what the user is allowed to s
|
|
|
716
755
|
| `useLtAi()` | One-shot `prompt(input)` and streaming `promptStream(input, handlers)` (POST `/ai/stream`) |
|
|
717
756
|
| `useLtAiChat()` | Multi-turn chat state, streaming, budget, confirmation, `stop()`/`clear()`, optional `maxMessages` cap, auto-stop on component unmount |
|
|
718
757
|
| `useLtAiConnections()` | User self-service: available connections + `select()` (`selected`/`locked`) |
|
|
758
|
+
| `useLtAiSettings()` | The user's own AI settings: the MCP confirmation switch + `update()` (`enforced`) |
|
|
719
759
|
| `useLtAiUsage()` | Full token/prompt usage breakdown (`GET /ai/usage`) |
|
|
720
760
|
| `useLtAiPrompts()` | User-facing CRUD for re-usable prompt snippets ("Vorlagen", `scope: 'user'` / `'tenant'`) |
|
|
721
761
|
| `useLtAiPlaceholders()` | Loads `{{placeholder}}` registry from the backend so slot / prompt editors render a dynamic helper sidebar |
|
|
@@ -829,6 +869,7 @@ The package works **with or without** `@nuxtjs/i18n`:
|
|
|
829
869
|
| `useLtAi()` | One-shot `prompt()` + streaming `promptStream()` for the nest-server AI module |
|
|
830
870
|
| `useLtAiChat()` | Multi-turn chat state, streaming, budget summary, confirmation gate, `maxMessages` cap |
|
|
831
871
|
| `useLtAiConnections()` | User self-service connection list + `select()` |
|
|
872
|
+
| `useLtAiSettings()` | The user's own AI settings (MCP confirmation switch, nest-server >= 12.3.0) |
|
|
832
873
|
| `useLtAiUsage()` | Token/prompt usage breakdown per user / tenant |
|
|
833
874
|
| `useLtAiPrompts()` | User-facing prompt snippet CRUD (`'user'` / `'tenant'` scope) |
|
|
834
875
|
| `useLtAiPlaceholders()` | Loads the backend's `{{placeholder}}` registry |
|
package/dist/module.d.mts
CHANGED
|
@@ -3,7 +3,7 @@ import { LtExtensionsModuleOptions } from '../dist/runtime/types/index.js';
|
|
|
3
3
|
export * from '../dist/runtime/types/index.js';
|
|
4
4
|
|
|
5
5
|
declare const name = "@lenne.tech/nuxt-extensions";
|
|
6
|
-
declare const version = "1.
|
|
6
|
+
declare const version = "1.22.0";
|
|
7
7
|
declare const configKey = "ltExtensions";
|
|
8
8
|
declare const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
9
9
|
declare const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { defineNuxtModule, createResolver, tryResolveModule, addImports, addComp
|
|
|
2
2
|
import { pathToFileURL } from 'node:url';
|
|
3
3
|
|
|
4
4
|
const name = "@lenne.tech/nuxt-extensions";
|
|
5
|
-
const version = "1.
|
|
5
|
+
const version = "1.22.0";
|
|
6
6
|
const configKey = "ltExtensions";
|
|
7
7
|
const DEFAULT_LT_AUTH_STATE_COOKIE = "lt-auth-state";
|
|
8
8
|
const DEFAULT_LT_JWT_TOKEN_COOKIE = "lt-jwt-token";
|
|
@@ -204,6 +204,7 @@ const module$1 = defineNuxtModule({
|
|
|
204
204
|
{ name: "useLtAi", from: resolve("./runtime/composables/use-lt-ai") },
|
|
205
205
|
{ name: "useLtAiChat", from: resolve("./runtime/composables/use-lt-ai-chat") },
|
|
206
206
|
{ name: "useLtAiConnections", from: resolve("./runtime/composables/use-lt-ai-connections") },
|
|
207
|
+
{ name: "useLtAiSettings", from: resolve("./runtime/composables/use-lt-ai-settings") },
|
|
207
208
|
{ name: "useLtAiUsage", from: resolve("./runtime/composables/use-lt-ai-usage") },
|
|
208
209
|
{ name: "useLtAiPrompts", from: resolve("./runtime/composables/use-lt-ai-prompts") },
|
|
209
210
|
{ name: "useLtAiPlaceholders", from: resolve("./runtime/composables/use-lt-ai-placeholders") },
|
|
@@ -8,6 +8,7 @@ export { useLtErrorTranslation } from './use-lt-error-translation.js';
|
|
|
8
8
|
export { useLtAi } from './use-lt-ai.js';
|
|
9
9
|
export { useLtAiChat } from './use-lt-ai-chat.js';
|
|
10
10
|
export { useLtAiConnections } from './use-lt-ai-connections.js';
|
|
11
|
+
export { useLtAiSettings } from './use-lt-ai-settings.js';
|
|
11
12
|
export { useLtAiUsage } from './use-lt-ai-usage.js';
|
|
12
13
|
export { useLtAiAdmin } from './use-lt-ai-admin.js';
|
|
13
14
|
export { useLtAiPrompts } from './use-lt-ai-prompts.js';
|
|
@@ -8,6 +8,7 @@ export { useLtErrorTranslation } from "./use-lt-error-translation.js";
|
|
|
8
8
|
export { useLtAi } from "./use-lt-ai.js";
|
|
9
9
|
export { useLtAiChat } from "./use-lt-ai-chat.js";
|
|
10
10
|
export { useLtAiConnections } from "./use-lt-ai-connections.js";
|
|
11
|
+
export { useLtAiSettings } from "./use-lt-ai-settings.js";
|
|
11
12
|
export { useLtAiUsage } from "./use-lt-ai-usage.js";
|
|
12
13
|
export { useLtAiAdmin } from "./use-lt-ai-admin.js";
|
|
13
14
|
export { useLtAiPrompts } from "./use-lt-ai-prompts.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User self-service for the current user's own AI settings, today whether MCP
|
|
3
|
+
* asks before a destructive or mutating tool runs (a "bypass permissions"
|
|
4
|
+
* switch). Backed by `GET /ai/settings` and `PUT /ai/settings`
|
|
5
|
+
* (nest-server >= 12.3.0).
|
|
6
|
+
*
|
|
7
|
+
* The answer is always the setting as it takes effect. When the server fixes
|
|
8
|
+
* it (`ai.mcp.confirmation.enforced`), `enforced` is true, the switch should be
|
|
9
|
+
* shown disabled, and `update()` is refused with 403 (`#LTNS_0609`).
|
|
10
|
+
*/
|
|
11
|
+
import type { UseLtAiSettingsReturn } from '../types/ai.js';
|
|
12
|
+
export declare function useLtAiSettings(): UseLtAiSettingsReturn;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { computed, readonly, ref } from "#imports";
|
|
2
|
+
import { ltAiRequest } from "../lib/ai.js";
|
|
3
|
+
export function useLtAiSettings() {
|
|
4
|
+
const settings = ref(null);
|
|
5
|
+
const loading = ref(false);
|
|
6
|
+
const error = ref(null);
|
|
7
|
+
const enforced = computed(() => settings.value?.mcpConfirmationEnforced === true);
|
|
8
|
+
async function load() {
|
|
9
|
+
loading.value = true;
|
|
10
|
+
error.value = null;
|
|
11
|
+
try {
|
|
12
|
+
settings.value = await ltAiRequest("GET", "/settings");
|
|
13
|
+
} catch (err) {
|
|
14
|
+
error.value = err.message;
|
|
15
|
+
} finally {
|
|
16
|
+
loading.value = false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async function update(input) {
|
|
20
|
+
loading.value = true;
|
|
21
|
+
error.value = null;
|
|
22
|
+
try {
|
|
23
|
+
settings.value = await ltAiRequest("PUT", "/settings", input);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
error.value = err.message;
|
|
26
|
+
throw err;
|
|
27
|
+
} finally {
|
|
28
|
+
loading.value = false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { enforced, error: readonly(error), load, loading: readonly(loading), settings: readonly(settings), update };
|
|
32
|
+
}
|
|
@@ -126,6 +126,17 @@ export interface LtAiAvailableConnection {
|
|
|
126
126
|
name?: string;
|
|
127
127
|
selected?: boolean;
|
|
128
128
|
}
|
|
129
|
+
/** The current user's AI settings as they take effect (`GET/PUT /ai/settings`, nest-server >= 12.3.0). */
|
|
130
|
+
export interface LtAiSettings {
|
|
131
|
+
/** Whether MCP asks this user before a destructive tool (and a mutating one, if configured) runs. */
|
|
132
|
+
mcpConfirmation: boolean;
|
|
133
|
+
/** Whether the server fixed `mcpConfirmation`, so the user cannot change it (a change answers 403 `#LTNS_0609`). */
|
|
134
|
+
mcpConfirmationEnforced: boolean;
|
|
135
|
+
}
|
|
136
|
+
/** The user's own choices for `PUT /ai/settings`; a field left out stays unchanged. */
|
|
137
|
+
export interface LtAiSettingsInput {
|
|
138
|
+
mcpConfirmation?: boolean;
|
|
139
|
+
}
|
|
129
140
|
/** Usage for a single scope (user or tenant). */
|
|
130
141
|
export interface LtAiUsageScope {
|
|
131
142
|
maxPrompts?: number;
|
|
@@ -429,6 +440,16 @@ export interface UseLtAiConnectionsReturn {
|
|
|
429
440
|
select: (connectionId: string) => Promise<void>;
|
|
430
441
|
selected: ComputedRef<LtAiAvailableConnection | undefined>;
|
|
431
442
|
}
|
|
443
|
+
export interface UseLtAiSettingsReturn {
|
|
444
|
+
/** True when the server fixed the MCP confirmation: show the switch disabled. */
|
|
445
|
+
enforced: ComputedRef<boolean>;
|
|
446
|
+
error: DeepReadonly<Ref<null | string>>;
|
|
447
|
+
load: () => Promise<void>;
|
|
448
|
+
loading: DeepReadonly<Ref<boolean>>;
|
|
449
|
+
settings: DeepReadonly<Ref<LtAiSettings | null>>;
|
|
450
|
+
/** Stores the user's choice and adopts the answer; throws (and sets `error`) when refused. */
|
|
451
|
+
update: (input: LtAiSettingsInput) => Promise<void>;
|
|
452
|
+
}
|
|
432
453
|
export interface UseLtAiUsageReturn {
|
|
433
454
|
error: DeepReadonly<Ref<null | string>>;
|
|
434
455
|
loading: DeepReadonly<Ref<boolean>>;
|
|
@@ -3,4 +3,4 @@ export type { LtFileInfo, LtUploadItem, LtUploadOptions, LtUploadProgress, LtUpl
|
|
|
3
3
|
export type { LtAiModuleOptions, LtAuthCookieNamesOptions, LtAuthModuleOptions, LtErrorTranslationModuleOptions, LtExtensionsModuleOptions, LtExtensionsPublicRuntimeConfig, LtFormLabelAssociationOptions, LtI18nModuleOptions, LtPreHydrationInputOptions, LtSystemSetupModuleOptions, LtTusModuleOptions, } from './module.js';
|
|
4
4
|
export type { LtErrorTranslationResponse, LtParsedError, UseLtErrorTranslationReturn } from './error.js';
|
|
5
5
|
export type { LtSyncConflict, LtSyncConflictResolution, LtSyncIdFormat, LtSyncItemStatus, LtSyncModuleOptions, LtSyncPushResult, LtSyncRecord, LtSyncResult, UseLtOfflineSyncOptions, UseLtOfflineSyncReturn, } from './sync.js';
|
|
6
|
-
export type { LtAiAction, LtAiAvailableConnection, LtAiBudgetLimit, LtAiBudgetSummary, LtAiConnection, LtAiConnectionInput, LtAiConnectionPreference, LtAiEffectiveSlot, LtAiInteraction, LtAiMessage, LtAiMode, LtAiPlaceholder, LtAiPrompt, LtAiPromptHint, LtAiPromptHintInput, LtAiPromptInput, LtAiPromptRunInput, LtAiResponse, LtAiSlot, LtAiSlotInput, LtAiStreamEvent, LtAiStreamHandlers, LtAiUsage, LtAiUsageInfo, LtAiUsageScope, UseLtAiAdminReturn, UseLtAiChatOptions, UseLtAiChatReturn, UseLtAiConnectionsReturn, UseLtAiPlaceholdersReturn, UseLtAiPromptsReturn, UseLtAiReturn, UseLtAiUsageReturn, } from './ai.js';
|
|
6
|
+
export type { LtAiAction, LtAiAvailableConnection, LtAiBudgetLimit, LtAiBudgetSummary, LtAiConnection, LtAiConnectionInput, LtAiConnectionPreference, LtAiEffectiveSlot, LtAiInteraction, LtAiMessage, LtAiMode, LtAiPlaceholder, LtAiPrompt, LtAiPromptHint, LtAiPromptHintInput, LtAiPromptInput, LtAiPromptRunInput, LtAiResponse, LtAiSettings, LtAiSettingsInput, LtAiSlot, LtAiSlotInput, LtAiStreamEvent, LtAiStreamHandlers, LtAiUsage, LtAiUsageInfo, LtAiUsageScope, UseLtAiAdminReturn, UseLtAiChatOptions, UseLtAiChatReturn, UseLtAiConnectionsReturn, UseLtAiPlaceholdersReturn, UseLtAiPromptsReturn, UseLtAiReturn, UseLtAiSettingsReturn, UseLtAiUsageReturn, } from './ai.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nuxt-extensions",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"description": "Reusable Nuxt 4 composables, components, and Better-Auth integration for lenne.tech projects",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -102,24 +102,24 @@
|
|
|
102
102
|
"devDependencies": {
|
|
103
103
|
"@better-auth/passkey": "1.7.12",
|
|
104
104
|
"@nuxt/module-builder": "1.0.3",
|
|
105
|
-
"@nuxt/schema": "4.6.
|
|
105
|
+
"@nuxt/schema": "4.6.4",
|
|
106
106
|
"@nuxt/ui": "4.12.0",
|
|
107
107
|
"@playwright/test": "1.64.1",
|
|
108
108
|
"@types/node": "26.6.6",
|
|
109
|
-
"@vitest/coverage-v8": "5.0
|
|
109
|
+
"@vitest/coverage-v8": "5.1.0",
|
|
110
110
|
"@vue/server-renderer": "3.5.44",
|
|
111
111
|
"better-auth": "1.7.12",
|
|
112
112
|
"cross-spawn": "7.0.6",
|
|
113
113
|
"fake-indexeddb": "6.2.7",
|
|
114
114
|
"happy-dom": "21.0.4",
|
|
115
|
-
"nuxt": "4.6.
|
|
115
|
+
"nuxt": "4.6.4",
|
|
116
116
|
"oxfmt": "0.75.0",
|
|
117
117
|
"oxlint": "1.90.0",
|
|
118
118
|
"oxlint-tsgolint": "7.0.2005",
|
|
119
119
|
"reka-ui": "2.11.0",
|
|
120
120
|
"tus-js-client": "4.3.1",
|
|
121
121
|
"typescript": "5.9.3",
|
|
122
|
-
"vitest": "5.0
|
|
122
|
+
"vitest": "5.1.0",
|
|
123
123
|
"vue-tsc": "3.3.14"
|
|
124
124
|
},
|
|
125
125
|
"keywords": [
|