@autumnsgrove/groveengine 0.9.85 → 0.9.91
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/components/admin/GutterManager.svelte +0 -1
- package/dist/components/admin/MarkdownEditor.svelte +5 -2
- package/dist/components/custom/TableOfContents.svelte +24 -6
- package/dist/config/wisp.d.ts +4 -42
- package/dist/config/wisp.js +2 -77
- package/dist/curios/timeline/Timeline.svelte +35 -17
- package/dist/curios/timeline/index.d.ts +1 -1
- package/dist/curios/timeline/index.js +1 -1
- package/dist/curios/timeline/providers/openrouter.d.ts +0 -8
- package/dist/curios/timeline/providers/openrouter.js +1 -72
- package/dist/durable-objects/TenantDO.d.ts +7 -106
- package/dist/durable-objects/TenantDO.js +7 -423
- package/dist/durable-objects/index.d.ts +3 -3
- package/dist/durable-objects/index.js +3 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -3
- package/dist/lumen/client.d.ts +113 -0
- package/dist/lumen/client.js +358 -0
- package/dist/lumen/config.d.ts +113 -0
- package/dist/lumen/config.js +269 -0
- package/dist/lumen/errors.d.ts +50 -0
- package/dist/lumen/errors.js +92 -0
- package/dist/lumen/index.d.ts +68 -0
- package/dist/lumen/index.js +97 -0
- package/dist/lumen/mcp.d.ts +75 -0
- package/dist/lumen/mcp.js +92 -0
- package/dist/lumen/pipeline/postprocessor.d.ts +106 -0
- package/dist/lumen/pipeline/postprocessor.js +108 -0
- package/dist/lumen/pipeline/preprocessor.d.ts +62 -0
- package/dist/lumen/pipeline/preprocessor.js +208 -0
- package/dist/lumen/providers/cloudflare-ai.d.ts +39 -0
- package/dist/lumen/providers/cloudflare-ai.js +202 -0
- package/dist/lumen/providers/index.d.ts +28 -0
- package/dist/lumen/providers/index.js +46 -0
- package/dist/lumen/providers/openrouter.d.ts +56 -0
- package/dist/lumen/providers/openrouter.js +389 -0
- package/dist/lumen/providers/types.d.ts +90 -0
- package/dist/lumen/providers/types.js +7 -0
- package/dist/lumen/quota/limits.d.ts +50 -0
- package/dist/lumen/quota/limits.js +127 -0
- package/dist/lumen/quota/tracker.d.ts +94 -0
- package/dist/lumen/quota/tracker.js +216 -0
- package/dist/lumen/router.d.ts +65 -0
- package/dist/lumen/router.js +238 -0
- package/dist/lumen/shutter.d.ts +39 -0
- package/dist/lumen/shutter.js +42 -0
- package/dist/lumen/songbird.d.ts +27 -0
- package/dist/lumen/songbird.js +178 -0
- package/dist/lumen/types.d.ts +340 -0
- package/dist/lumen/types.js +9 -0
- package/dist/server/encryption.js +3 -3
- package/dist/server/env-validation.d.ts +2 -6
- package/dist/server/env-validation.js +1 -3
- package/dist/server/inference-client.d.ts +3 -46
- package/dist/server/inference-client.js +3 -135
- package/dist/server/petal/lumen-classify.d.ts +26 -0
- package/dist/server/petal/lumen-classify.js +144 -0
- package/dist/server/services/__mocks__/cloudflare.js +158 -57
- package/dist/server/services/index.d.ts +2 -0
- package/dist/server/services/index.js +9 -0
- package/dist/server/services/og-fetcher.d.ts +60 -0
- package/dist/server/services/og-fetcher.js +415 -0
- package/dist/thorn/config.d.ts +31 -0
- package/dist/thorn/config.js +149 -0
- package/dist/thorn/index.d.ts +44 -0
- package/dist/thorn/index.js +45 -0
- package/dist/thorn/moderate.d.ts +35 -0
- package/dist/thorn/moderate.js +54 -0
- package/dist/thorn/types.d.ts +107 -0
- package/dist/thorn/types.js +9 -0
- package/dist/types/og.d.ts +65 -0
- package/dist/types/og.js +9 -0
- package/dist/ui/components/chrome/defaults.js +2 -2
- package/dist/ui/components/content/LinkPreview.svelte +353 -0
- package/dist/ui/components/content/LinkPreview.svelte.d.ts +63 -0
- package/dist/ui/components/content/index.d.ts +2 -1
- package/dist/ui/components/content/index.js +4 -3
- package/dist/ui/vineyard/AuthButton.svelte +127 -0
- package/dist/ui/vineyard/AuthButton.svelte.d.ts +6 -0
- package/dist/ui/vineyard/CodeExample.svelte +186 -0
- package/dist/ui/vineyard/CodeExample.svelte.d.ts +8 -0
- package/dist/ui/vineyard/DemoContainer.svelte +139 -0
- package/dist/ui/vineyard/DemoContainer.svelte.d.ts +8 -0
- package/dist/ui/vineyard/FeatureCard.svelte +127 -0
- package/dist/ui/vineyard/FeatureCard.svelte.d.ts +8 -0
- package/dist/ui/vineyard/RoadmapSection.svelte +238 -0
- package/dist/ui/vineyard/RoadmapSection.svelte.d.ts +4 -0
- package/dist/ui/vineyard/StatusBadge.svelte +88 -0
- package/dist/ui/vineyard/StatusBadge.svelte.d.ts +4 -0
- package/dist/ui/vineyard/TierGate.svelte +172 -0
- package/dist/ui/vineyard/TierGate.svelte.d.ts +10 -0
- package/dist/ui/vineyard/UserMenu.svelte +289 -0
- package/dist/ui/vineyard/UserMenu.svelte.d.ts +6 -0
- package/dist/ui/vineyard/VineyardLayout.svelte +254 -0
- package/dist/ui/vineyard/VineyardLayout.svelte.d.ts +8 -0
- package/dist/ui/vineyard/auth.d.ts +81 -0
- package/dist/ui/vineyard/auth.js +134 -0
- package/dist/ui/vineyard/index.d.ts +15 -6
- package/dist/ui/vineyard/index.js +21 -7
- package/dist/ui/vineyard/types.d.ts +147 -0
- package/dist/ui/vineyard/types.js +5 -0
- package/dist/utils/markdown.d.ts +6 -0
- package/dist/utils/markdown.js +50 -44
- package/package.json +12 -3
- package/static/apple-touch-icon.png +0 -0
- package/static/favicon-32x32.png +0 -0
- package/static/favicon.svg +27 -16
- package/static/icon-192.png +0 -0
- package/static/icon-512.png +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
<script>
|
|
2
|
-
import
|
|
2
|
+
import MarkdownIt from "markdown-it";
|
|
3
3
|
import { onMount, tick } from "svelte";
|
|
4
4
|
import { sanitizeMarkdown } from "../../utils/sanitize";
|
|
5
|
+
|
|
6
|
+
// Local instance for admin editor preview
|
|
7
|
+
const editorMd = new MarkdownIt({ html: false, linkify: true });
|
|
5
8
|
import { extractHeaders } from "../../utils/markdown";
|
|
6
9
|
import "../../styles/content.css";
|
|
7
10
|
import { Button, Input, Logo } from '../../ui';
|
|
@@ -111,7 +114,7 @@
|
|
|
111
114
|
let wordCount = $derived(content.trim() ? content.trim().split(/\s+/).length : 0);
|
|
112
115
|
let charCount = $derived(content.length);
|
|
113
116
|
let lineCount = $derived(content.split("\n").length);
|
|
114
|
-
let previewHtml = $derived(content ? sanitizeMarkdown(
|
|
117
|
+
let previewHtml = $derived(content ? sanitizeMarkdown(editorMd.render(content)) : "");
|
|
115
118
|
let previewHeaders = $derived(content ? extractHeaders(content) : []);
|
|
116
119
|
|
|
117
120
|
let readingTime = $derived.by(() => {
|
|
@@ -102,8 +102,9 @@
|
|
|
102
102
|
<style>
|
|
103
103
|
.toc {
|
|
104
104
|
position: sticky;
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
/* Account for sticky navbar height + divider line + breathing room */
|
|
106
|
+
top: 6.5rem;
|
|
107
|
+
max-height: calc(100vh - 7.5rem);
|
|
107
108
|
overflow-y: auto;
|
|
108
109
|
padding: 1.25rem;
|
|
109
110
|
font-size: 0.875rem;
|
|
@@ -214,15 +215,32 @@
|
|
|
214
215
|
.level-6 .toc-link {
|
|
215
216
|
padding-left: 4rem;
|
|
216
217
|
}
|
|
217
|
-
/* Scrollbar styling */
|
|
218
|
+
/* Scrollbar styling - hidden by default, visible on hover */
|
|
219
|
+
.toc {
|
|
220
|
+
/* Firefox scrollbar hiding */
|
|
221
|
+
scrollbar-width: thin;
|
|
222
|
+
scrollbar-color: transparent transparent;
|
|
223
|
+
}
|
|
224
|
+
.toc:hover {
|
|
225
|
+
scrollbar-color: var(--color-foreground-subtle, rgba(0, 0, 0, 0.2)) transparent;
|
|
226
|
+
}
|
|
227
|
+
/* Webkit (Chrome, Safari, Edge) scrollbar styling */
|
|
218
228
|
.toc::-webkit-scrollbar {
|
|
219
|
-
width:
|
|
229
|
+
width: 6px;
|
|
220
230
|
}
|
|
221
231
|
.toc::-webkit-scrollbar-track {
|
|
222
232
|
background: transparent;
|
|
223
233
|
}
|
|
224
234
|
.toc::-webkit-scrollbar-thumb {
|
|
225
|
-
background:
|
|
226
|
-
border-radius:
|
|
235
|
+
background: transparent;
|
|
236
|
+
border-radius: 3px;
|
|
237
|
+
-webkit-transition: background 0.2s ease;
|
|
238
|
+
transition: background 0.2s ease;
|
|
239
|
+
}
|
|
240
|
+
.toc:hover::-webkit-scrollbar-thumb {
|
|
241
|
+
background: var(--color-foreground-subtle, rgba(0, 0, 0, 0.2));
|
|
242
|
+
}
|
|
243
|
+
:global(.dark) .toc:hover::-webkit-scrollbar-thumb {
|
|
244
|
+
background: rgba(255, 255, 255, 0.15);
|
|
227
245
|
}
|
|
228
246
|
</style>
|
package/dist/config/wisp.d.ts
CHANGED
|
@@ -1,37 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wisp - Grove Writing Assistant Configuration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Rate limits, content constraints, and prompt modes for the Wisp writing assistant.
|
|
5
|
+
* AI inference is now handled by Lumen (see $lib/lumen).
|
|
6
6
|
*
|
|
7
7
|
* @see docs/specs/writing-assistant-unified-spec.md
|
|
8
8
|
*/
|
|
9
|
-
export interface ProviderConfig {
|
|
10
|
-
name: string;
|
|
11
|
-
baseUrl: string;
|
|
12
|
-
role: 'primary' | 'backup' | 'tertiary';
|
|
13
|
-
zdr: boolean;
|
|
14
|
-
models: Record<string, string>;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Approved inference providers with ZDR support
|
|
18
|
-
* Order determines fallback priority
|
|
19
|
-
*/
|
|
20
|
-
export declare const PROVIDERS: Record<string, ProviderConfig>;
|
|
21
|
-
export interface ModelFallback {
|
|
22
|
-
provider: string;
|
|
23
|
-
model: string;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Model fallback cascade
|
|
27
|
-
* Try in order until one succeeds
|
|
28
|
-
*/
|
|
29
|
-
export declare const MODEL_FALLBACK_CASCADE: ModelFallback[];
|
|
30
|
-
export interface ModelPricing {
|
|
31
|
-
input: number;
|
|
32
|
-
output: number;
|
|
33
|
-
}
|
|
34
|
-
export declare const MODEL_PRICING: Record<string, ModelPricing>;
|
|
35
9
|
/** Maximum content length for analysis (characters) */
|
|
36
10
|
export declare const MAX_CONTENT_LENGTH = 50000;
|
|
37
11
|
/** Max output tokens by analysis type and mode */
|
|
@@ -61,8 +35,8 @@ export declare const COST_CAP: {
|
|
|
61
35
|
readonly maxCostUSD: 5;
|
|
62
36
|
readonly warningThreshold: 0.8;
|
|
63
37
|
};
|
|
64
|
-
export type AnalysisMode =
|
|
65
|
-
export type AnalysisAction =
|
|
38
|
+
export type AnalysisMode = "quick" | "thorough";
|
|
39
|
+
export type AnalysisAction = "grammar" | "tone" | "readability";
|
|
66
40
|
/**
|
|
67
41
|
* Prompt modes control analysis depth without changing models
|
|
68
42
|
*/
|
|
@@ -80,18 +54,6 @@ export declare const PROMPT_MODES: {
|
|
|
80
54
|
readonly maxOutputMultiplier: 2;
|
|
81
55
|
};
|
|
82
56
|
};
|
|
83
|
-
/**
|
|
84
|
-
* Calculate cost for token usage
|
|
85
|
-
*/
|
|
86
|
-
export declare function calculateCost(model: string, inputTokens: number, outputTokens: number): number;
|
|
87
|
-
/**
|
|
88
|
-
* Get the model ID for a provider
|
|
89
|
-
*/
|
|
90
|
-
export declare function getModelId(provider: string, model: string): string | null;
|
|
91
|
-
/**
|
|
92
|
-
* Get provider configuration
|
|
93
|
-
*/
|
|
94
|
-
export declare function getProvider(provider: string): ProviderConfig | null;
|
|
95
57
|
/**
|
|
96
58
|
* Get max tokens for an action and mode
|
|
97
59
|
*/
|
package/dist/config/wisp.js
CHANGED
|
@@ -1,65 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Wisp - Grove Writing Assistant Configuration
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Rate limits, content constraints, and prompt modes for the Wisp writing assistant.
|
|
5
|
+
* AI inference is now handled by Lumen (see $lib/lumen).
|
|
6
6
|
*
|
|
7
7
|
* @see docs/specs/writing-assistant-unified-spec.md
|
|
8
8
|
*/
|
|
9
|
-
/**
|
|
10
|
-
* Approved inference providers with ZDR support
|
|
11
|
-
* Order determines fallback priority
|
|
12
|
-
*/
|
|
13
|
-
export const PROVIDERS = {
|
|
14
|
-
fireworks: {
|
|
15
|
-
name: "Fireworks AI",
|
|
16
|
-
baseUrl: "https://api.fireworks.ai/inference/v1",
|
|
17
|
-
role: "primary",
|
|
18
|
-
zdr: true, // Zero Data Retention default for open models
|
|
19
|
-
models: {
|
|
20
|
-
"deepseek-v3.2": "accounts/fireworks/models/deepseek-v3p2",
|
|
21
|
-
"kimi-k2": "accounts/fireworks/models/kimi-k2-instruct-0905",
|
|
22
|
-
"llama-3.1-70b": "accounts/fireworks/models/llama-v3p1-70b-instruct",
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
cerebras: {
|
|
26
|
-
name: "Cerebras",
|
|
27
|
-
baseUrl: "https://api.cerebras.ai/v1",
|
|
28
|
-
role: "backup",
|
|
29
|
-
zdr: true, // US-based, zero retention
|
|
30
|
-
models: {
|
|
31
|
-
"llama-3.3-70b": "llama-3.3-70b",
|
|
32
|
-
"gpt-oss-120b": "gpt-oss-120b",
|
|
33
|
-
},
|
|
34
|
-
},
|
|
35
|
-
groq: {
|
|
36
|
-
name: "Groq",
|
|
37
|
-
baseUrl: "https://api.groq.com/openai/v1",
|
|
38
|
-
role: "tertiary",
|
|
39
|
-
zdr: true, // Explicit ZDR toggle
|
|
40
|
-
models: {
|
|
41
|
-
"llama-3.3-70b": "llama-3.3-70b-versatile",
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Model fallback cascade
|
|
47
|
-
* Try in order until one succeeds
|
|
48
|
-
*/
|
|
49
|
-
export const MODEL_FALLBACK_CASCADE = [
|
|
50
|
-
{ provider: "fireworks", model: "deepseek-v3.2" },
|
|
51
|
-
{ provider: "fireworks", model: "kimi-k2" },
|
|
52
|
-
{ provider: "fireworks", model: "llama-3.1-70b" },
|
|
53
|
-
{ provider: "cerebras", model: "llama-3.3-70b" },
|
|
54
|
-
{ provider: "groq", model: "llama-3.3-70b" },
|
|
55
|
-
];
|
|
56
|
-
export const MODEL_PRICING = {
|
|
57
|
-
"deepseek-v3.2": { input: 0.56, output: 1.68 },
|
|
58
|
-
"kimi-k2": { input: 0.6, output: 2.5 },
|
|
59
|
-
"llama-3.1-70b": { input: 0.9, output: 0.9 },
|
|
60
|
-
"llama-3.3-70b": { input: 0.59, output: 0.79 },
|
|
61
|
-
"gpt-oss-120b": { input: 0.25, output: 0.69 },
|
|
62
|
-
};
|
|
63
9
|
// ============================================================================
|
|
64
10
|
// Limits & Thresholds
|
|
65
11
|
// ============================================================================
|
|
@@ -106,27 +52,6 @@ export const PROMPT_MODES = {
|
|
|
106
52
|
// ============================================================================
|
|
107
53
|
// Helper Functions
|
|
108
54
|
// ============================================================================
|
|
109
|
-
/**
|
|
110
|
-
* Calculate cost for token usage
|
|
111
|
-
*/
|
|
112
|
-
export function calculateCost(model, inputTokens, outputTokens) {
|
|
113
|
-
const pricing = MODEL_PRICING[model] || MODEL_PRICING["deepseek-v3.2"];
|
|
114
|
-
const cost = (inputTokens * pricing.input + outputTokens * pricing.output) / 1_000_000;
|
|
115
|
-
// Round to 6 decimal places to avoid floating point precision issues
|
|
116
|
-
return Math.round(cost * 1_000_000) / 1_000_000;
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Get the model ID for a provider
|
|
120
|
-
*/
|
|
121
|
-
export function getModelId(provider, model) {
|
|
122
|
-
return PROVIDERS[provider]?.models[model] || null;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Get provider configuration
|
|
126
|
-
*/
|
|
127
|
-
export function getProvider(provider) {
|
|
128
|
-
return PROVIDERS[provider] || null;
|
|
129
|
-
}
|
|
130
55
|
/**
|
|
131
56
|
* Get max tokens for an action and mode
|
|
132
57
|
*/
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
* - Pagination with "Load More"
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import
|
|
13
|
+
import MarkdownIt from 'markdown-it';
|
|
14
14
|
import { sanitizeMarkdown } from '../../utils';
|
|
15
|
+
|
|
16
|
+
// Local instance with breaks: true for timeline rendering
|
|
17
|
+
const timelineMd = new MarkdownIt({ breaks: true, linkify: true });
|
|
15
18
|
import {
|
|
16
19
|
Calendar,
|
|
17
20
|
GitCommit,
|
|
@@ -76,12 +79,6 @@
|
|
|
76
79
|
let loadingMore = $state(false);
|
|
77
80
|
let expandedCards = $state(new Set<string>());
|
|
78
81
|
|
|
79
|
-
// Configure marked for safe rendering
|
|
80
|
-
$effect(() => {
|
|
81
|
-
marked.setOptions({
|
|
82
|
-
breaks: true
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
82
|
|
|
86
83
|
// Fun rest day messages
|
|
87
84
|
const REST_DAY_MESSAGES = [
|
|
@@ -168,7 +165,7 @@
|
|
|
168
165
|
let withRepoLinks = text;
|
|
169
166
|
if (githubUsername) {
|
|
170
167
|
withRepoLinks = text.replace(
|
|
171
|
-
/^### (.+)$/gm,
|
|
168
|
+
/^### (?!\[)(.+)$/gm,
|
|
172
169
|
(match, repoName) => {
|
|
173
170
|
const cleanName = repoName.trim();
|
|
174
171
|
return `### [${cleanName}](https://github.com/${githubUsername}/${cleanName})`;
|
|
@@ -177,7 +174,7 @@
|
|
|
177
174
|
}
|
|
178
175
|
|
|
179
176
|
// Parse to HTML and sanitize
|
|
180
|
-
let html = sanitizeMarkdown(
|
|
177
|
+
let html = sanitizeMarkdown(timelineMd.render(withRepoLinks));
|
|
181
178
|
|
|
182
179
|
// Inject gutter comments after headers
|
|
183
180
|
for (const [headerName, items] of Object.entries(gutterByHeader)) {
|
|
@@ -310,12 +307,14 @@
|
|
|
310
307
|
<FolderGit2 size={14} />
|
|
311
308
|
{summary.repos_active?.join(', ') || 'Unknown'}
|
|
312
309
|
</span>
|
|
313
|
-
|
|
314
|
-
<
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
310
|
+
{#if summary.total_additions > 0 || summary.total_deletions > 0}
|
|
311
|
+
<span class="changes">
|
|
312
|
+
<Plus size={14} class="plus-icon" />
|
|
313
|
+
{summary.total_additions.toLocaleString()}
|
|
314
|
+
<Minus size={14} class="minus-icon" />
|
|
315
|
+
{summary.total_deletions.toLocaleString()}
|
|
316
|
+
</span>
|
|
317
|
+
{/if}
|
|
319
318
|
</div>
|
|
320
319
|
|
|
321
320
|
{#if summary.detailed_timeline && isExpanded}
|
|
@@ -442,11 +441,17 @@
|
|
|
442
441
|
font-weight: 600;
|
|
443
442
|
color: var(--color-foreground, #333);
|
|
444
443
|
}
|
|
444
|
+
:global(.dark) .date-full {
|
|
445
|
+
color: var(--bark, #f5f2ea);
|
|
446
|
+
}
|
|
445
447
|
.date-short {
|
|
446
448
|
display: none;
|
|
447
449
|
font-weight: 600;
|
|
448
450
|
color: var(--color-foreground, #333);
|
|
449
451
|
}
|
|
452
|
+
:global(.dark) .date-short {
|
|
453
|
+
color: var(--bark, #f5f2ea);
|
|
454
|
+
}
|
|
450
455
|
:global(.today-badge) {
|
|
451
456
|
background: var(--grove-500, #4ade80);
|
|
452
457
|
color: white;
|
|
@@ -519,13 +524,16 @@
|
|
|
519
524
|
font-size: 0.95rem;
|
|
520
525
|
}
|
|
521
526
|
:global(.dark) .rest-message {
|
|
522
|
-
color: #
|
|
527
|
+
color: var(--bark-600, #b69575);
|
|
523
528
|
}
|
|
524
529
|
.brief-summary {
|
|
525
530
|
margin: 0 0 0.75rem;
|
|
526
531
|
color: var(--color-foreground, #333);
|
|
527
532
|
line-height: 1.5;
|
|
528
533
|
}
|
|
534
|
+
:global(.dark) .brief-summary {
|
|
535
|
+
color: var(--bark, #f5f2ea);
|
|
536
|
+
}
|
|
529
537
|
.meta-info {
|
|
530
538
|
display: flex;
|
|
531
539
|
flex-wrap: wrap;
|
|
@@ -534,6 +542,9 @@
|
|
|
534
542
|
color: var(--color-foreground, #333);
|
|
535
543
|
margin-bottom: 0.75rem;
|
|
536
544
|
}
|
|
545
|
+
:global(.dark) .meta-info {
|
|
546
|
+
color: var(--bark-700, #ccb59c);
|
|
547
|
+
}
|
|
537
548
|
.repos, .changes {
|
|
538
549
|
display: flex;
|
|
539
550
|
align-items: center;
|
|
@@ -607,6 +618,7 @@
|
|
|
607
618
|
}
|
|
608
619
|
:global(.dark) .detailed-timeline {
|
|
609
620
|
background: var(--cream-300, #2a2a2a);
|
|
621
|
+
color: var(--bark, #f5f2ea);
|
|
610
622
|
}
|
|
611
623
|
.markdown-content :global(h2) {
|
|
612
624
|
font-size: 1.1rem;
|
|
@@ -625,6 +637,9 @@
|
|
|
625
637
|
margin: 1rem 0 0.5rem;
|
|
626
638
|
font-weight: 600;
|
|
627
639
|
}
|
|
640
|
+
:global(.dark) .markdown-content :global(h3) {
|
|
641
|
+
color: var(--bark, #f5f2ea);
|
|
642
|
+
}
|
|
628
643
|
.markdown-content :global(h3 a) {
|
|
629
644
|
color: #2c5f2d;
|
|
630
645
|
text-decoration: none;
|
|
@@ -647,6 +662,9 @@
|
|
|
647
662
|
margin-bottom: 0.25rem;
|
|
648
663
|
color: var(--color-foreground, #333);
|
|
649
664
|
}
|
|
665
|
+
:global(.dark) .markdown-content :global(li) {
|
|
666
|
+
color: var(--bark, #f5f2ea);
|
|
667
|
+
}
|
|
650
668
|
.markdown-content :global(code) {
|
|
651
669
|
background: var(--color-border-strong, #e0e0e0);
|
|
652
670
|
padding: 0.15rem 0.35rem;
|
|
@@ -681,7 +699,7 @@
|
|
|
681
699
|
}
|
|
682
700
|
:global(.dark) .timeline-footer {
|
|
683
701
|
border-top-color: var(--color-border-strong, #444);
|
|
684
|
-
color: #
|
|
702
|
+
color: var(--bark-700, #ccb59c);
|
|
685
703
|
}
|
|
686
704
|
|
|
687
705
|
/* Mobile Responsiveness */
|
|
@@ -15,7 +15,7 @@ import { type GutterComment as _GutterComment } from "./voices";
|
|
|
15
15
|
import type { ContextBrief as _ContextBrief, DetectedFocus as _DetectedFocus } from "./context";
|
|
16
16
|
export { default as Timeline } from "./Timeline.svelte";
|
|
17
17
|
export { default as Heatmap } from "./Heatmap.svelte";
|
|
18
|
-
export {
|
|
18
|
+
export { getOpenRouterModels, validateOpenRouterKey, OPENROUTER_MODELS, DEFAULT_OPENROUTER_MODEL, type OpenRouterModel, type OpenRouterResponse, type OpenRouterOptions, type OpenRouterKeyValidation, } from "./providers/openrouter";
|
|
19
19
|
export { buildVoicedPrompt, getAllVoices, getVoice, buildCustomVoice, VOICE_PRESETS, DEFAULT_VOICE, professional, quest, casual, poetic, minimal, type VoicePreset, type VoicePromptResult, type CustomVoiceConfig, type Commit, type GutterComment, type PromptContextInput, } from "./voices";
|
|
20
20
|
export { detectTaskFromText, detectTask, generateContextBrief, buildDetectedFocus, buildSummaryContextData, getHistoricalContext, detectContinuation, formatHistoricalContextForPrompt, formatContinuationForPrompt, buildPromptContext, type TaskType, type ContextBrief, type DetectedFocus, type TaskContinuation, type HistoricalContextEntry, type SummaryContextData, type PromptContext, } from "./context";
|
|
21
21
|
/**
|
|
@@ -24,7 +24,7 @@ export { default as Heatmap } from "./Heatmap.svelte";
|
|
|
24
24
|
// =============================================================================
|
|
25
25
|
// OpenRouter Provider
|
|
26
26
|
// =============================================================================
|
|
27
|
-
export {
|
|
27
|
+
export { getOpenRouterModels, validateOpenRouterKey, OPENROUTER_MODELS, DEFAULT_OPENROUTER_MODEL, } from "./providers/openrouter";
|
|
28
28
|
// =============================================================================
|
|
29
29
|
// Voice Presets
|
|
30
30
|
// =============================================================================
|
|
@@ -38,14 +38,6 @@ export interface OpenRouterKeyValidation {
|
|
|
38
38
|
*/
|
|
39
39
|
export declare const OPENROUTER_MODELS: Record<string, OpenRouterModel>;
|
|
40
40
|
export declare const DEFAULT_OPENROUTER_MODEL = "deepseek/deepseek-v3.2";
|
|
41
|
-
/**
|
|
42
|
-
* Call OpenRouter API
|
|
43
|
-
*/
|
|
44
|
-
export declare function callOpenRouter(apiKey: string, model: string, systemPrompt: string, userPrompt: string, options?: OpenRouterOptions): Promise<OpenRouterResponse>;
|
|
45
|
-
/**
|
|
46
|
-
* Calculate cost for OpenRouter request
|
|
47
|
-
*/
|
|
48
|
-
export declare function calculateOpenRouterCost(model: string, inputTokens: number, outputTokens: number): number;
|
|
49
41
|
/**
|
|
50
42
|
* Get list of recommended OpenRouter models for the UI
|
|
51
43
|
*/
|
|
@@ -89,79 +89,8 @@ export const OPENROUTER_MODELS = {
|
|
|
89
89
|
};
|
|
90
90
|
export const DEFAULT_OPENROUTER_MODEL = "deepseek/deepseek-v3.2";
|
|
91
91
|
// =============================================================================
|
|
92
|
-
// Utilities
|
|
92
|
+
// UI Utilities (kept for model picker and key validation)
|
|
93
93
|
// =============================================================================
|
|
94
|
-
/**
|
|
95
|
-
* Rough token estimation (4 chars ≈ 1 token)
|
|
96
|
-
*/
|
|
97
|
-
function estimateTokens(text) {
|
|
98
|
-
if (!text)
|
|
99
|
-
return 0;
|
|
100
|
-
return Math.ceil(text.length / 4);
|
|
101
|
-
}
|
|
102
|
-
// =============================================================================
|
|
103
|
-
// API Implementation
|
|
104
|
-
// =============================================================================
|
|
105
|
-
/**
|
|
106
|
-
* Call OpenRouter API
|
|
107
|
-
*/
|
|
108
|
-
export async function callOpenRouter(apiKey, model, systemPrompt, userPrompt, options = {}) {
|
|
109
|
-
const { maxTokens = 2048, temperature = 0.5, siteUrl = "https://grove.place", siteName = "Grove Timeline Curio", } = options;
|
|
110
|
-
const response = await fetch("https://openrouter.ai/api/v1/chat/completions", {
|
|
111
|
-
method: "POST",
|
|
112
|
-
headers: {
|
|
113
|
-
"Content-Type": "application/json",
|
|
114
|
-
Authorization: `Bearer ${apiKey}`,
|
|
115
|
-
"HTTP-Referer": siteUrl,
|
|
116
|
-
"X-Title": siteName,
|
|
117
|
-
},
|
|
118
|
-
body: JSON.stringify({
|
|
119
|
-
model: model,
|
|
120
|
-
messages: [
|
|
121
|
-
{ role: "system", content: systemPrompt },
|
|
122
|
-
{ role: "user", content: userPrompt },
|
|
123
|
-
],
|
|
124
|
-
max_tokens: maxTokens,
|
|
125
|
-
temperature: temperature,
|
|
126
|
-
}),
|
|
127
|
-
});
|
|
128
|
-
if (!response.ok) {
|
|
129
|
-
const errorText = await response.text();
|
|
130
|
-
let errorMessage = `OpenRouter API error (${response.status})`;
|
|
131
|
-
try {
|
|
132
|
-
const errorJson = JSON.parse(errorText);
|
|
133
|
-
if (errorJson.error?.message) {
|
|
134
|
-
errorMessage = `OpenRouter: ${errorJson.error.message}`;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
errorMessage += `: ${errorText.slice(0, 200)}`;
|
|
139
|
-
}
|
|
140
|
-
throw new Error(errorMessage);
|
|
141
|
-
}
|
|
142
|
-
const data = (await response.json());
|
|
143
|
-
const content = data.choices?.[0]?.message?.content || "";
|
|
144
|
-
const usage = data.usage || {};
|
|
145
|
-
return {
|
|
146
|
-
content,
|
|
147
|
-
inputTokens: usage.prompt_tokens || estimateTokens(systemPrompt + userPrompt),
|
|
148
|
-
outputTokens: usage.completion_tokens || estimateTokens(content),
|
|
149
|
-
model: data.model || model,
|
|
150
|
-
provider: "openrouter",
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Calculate cost for OpenRouter request
|
|
155
|
-
*/
|
|
156
|
-
export function calculateOpenRouterCost(model, inputTokens, outputTokens) {
|
|
157
|
-
const modelConfig = OPENROUTER_MODELS[model];
|
|
158
|
-
if (!modelConfig) {
|
|
159
|
-
return ((inputTokens + outputTokens) / 1_000_000) * 1.0;
|
|
160
|
-
}
|
|
161
|
-
const inputCost = (inputTokens / 1_000_000) * modelConfig.inputCostPer1M;
|
|
162
|
-
const outputCost = (outputTokens / 1_000_000) * modelConfig.outputCostPer1M;
|
|
163
|
-
return inputCost + outputCost;
|
|
164
|
-
}
|
|
165
94
|
/**
|
|
166
95
|
* Get list of recommended OpenRouter models for the UI
|
|
167
96
|
*/
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* TenantDO
|
|
2
|
+
* TenantDO Types
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - Analytics event buffering
|
|
4
|
+
* Type definitions for the TenantDO Durable Object.
|
|
5
|
+
* The actual class implementation lives in packages/durable-objects/src/TenantDO.ts
|
|
6
|
+
* and is deployed as a separate Cloudflare Worker (grove-durable-objects).
|
|
8
7
|
*
|
|
9
|
-
*
|
|
8
|
+
* The engine references this DO via service binding (script_name in wrangler.toml),
|
|
9
|
+
* so only types are needed here.
|
|
10
10
|
*
|
|
11
11
|
* Part of the Loom pattern - Grove's coordination layer.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
13
|
+
import type { PaidTierKey } from "../config/tiers.js";
|
|
14
14
|
export interface TenantConfig {
|
|
15
15
|
id: string;
|
|
16
16
|
subdomain: string;
|
|
@@ -42,102 +42,3 @@ export interface AnalyticsEvent {
|
|
|
42
42
|
data?: Record<string, unknown>;
|
|
43
43
|
timestamp: number;
|
|
44
44
|
}
|
|
45
|
-
export declare class TenantDO implements DurableObject {
|
|
46
|
-
private state;
|
|
47
|
-
private env;
|
|
48
|
-
private config;
|
|
49
|
-
private configLoadedAt;
|
|
50
|
-
private analyticsBuffer;
|
|
51
|
-
private initialized;
|
|
52
|
-
private refreshPromise;
|
|
53
|
-
private subdomain;
|
|
54
|
-
constructor(state: DurableObjectState, env: Env);
|
|
55
|
-
/**
|
|
56
|
-
* Initialize SQLite tables in DO storage
|
|
57
|
-
*/
|
|
58
|
-
private initializeStorage;
|
|
59
|
-
/**
|
|
60
|
-
* Main request handler - routes to appropriate method
|
|
61
|
-
*/
|
|
62
|
-
fetch(request: Request): Promise<Response>;
|
|
63
|
-
/**
|
|
64
|
-
* Get tenant config (cached in memory, refreshed from D1 if stale)
|
|
65
|
-
*
|
|
66
|
-
* Uses a promise lock pattern to prevent race conditions where multiple
|
|
67
|
-
* concurrent requests all trigger D1 queries simultaneously.
|
|
68
|
-
*/
|
|
69
|
-
private handleGetConfig;
|
|
70
|
-
/**
|
|
71
|
-
* Refresh config from DO storage or D1
|
|
72
|
-
*
|
|
73
|
-
* This method caches the tenant ID in the config to avoid repeated D1 lookups
|
|
74
|
-
* in hooks.server.ts. The ID is stored alongside other config data.
|
|
75
|
-
*/
|
|
76
|
-
private refreshConfig;
|
|
77
|
-
/**
|
|
78
|
-
* Update tenant config
|
|
79
|
-
*
|
|
80
|
-
* Uses the same promise lock pattern as handleGetConfig to prevent
|
|
81
|
-
* race conditions where concurrent updates could clobber each other.
|
|
82
|
-
*/
|
|
83
|
-
private handleUpdateConfig;
|
|
84
|
-
/**
|
|
85
|
-
* Get tier limits from centralized tiers.ts config
|
|
86
|
-
*
|
|
87
|
-
* This ensures TenantDO limits match the single source of truth,
|
|
88
|
-
* preventing drift between DO cached limits and actual tier configuration.
|
|
89
|
-
*/
|
|
90
|
-
private getTierLimits;
|
|
91
|
-
/**
|
|
92
|
-
* List all drafts for this tenant
|
|
93
|
-
*/
|
|
94
|
-
private handleListDrafts;
|
|
95
|
-
/**
|
|
96
|
-
* Get a specific draft
|
|
97
|
-
*/
|
|
98
|
-
private handleGetDraft;
|
|
99
|
-
/**
|
|
100
|
-
* Save or update a draft
|
|
101
|
-
*/
|
|
102
|
-
private handleSaveDraft;
|
|
103
|
-
/**
|
|
104
|
-
* Delete a draft
|
|
105
|
-
*/
|
|
106
|
-
private handleDeleteDraft;
|
|
107
|
-
/**
|
|
108
|
-
* Record an analytics event (buffered)
|
|
109
|
-
*/
|
|
110
|
-
private handleRecordEvent;
|
|
111
|
-
/**
|
|
112
|
-
* Alarm handler - flush analytics buffer
|
|
113
|
-
*/
|
|
114
|
-
alarm(): Promise<void>;
|
|
115
|
-
/**
|
|
116
|
-
* Flush analytics buffer to D1
|
|
117
|
-
*/
|
|
118
|
-
private flushAnalytics;
|
|
119
|
-
/**
|
|
120
|
-
* Get the subdomain for this tenant DO
|
|
121
|
-
*
|
|
122
|
-
* Priority:
|
|
123
|
-
* 1. Subdomain passed via X-Tenant-Subdomain header (set on every request)
|
|
124
|
-
* 2. Cached subdomain from previous request
|
|
125
|
-
* 3. Subdomain from cached config
|
|
126
|
-
*
|
|
127
|
-
* Returns null if subdomain is not yet known (shouldn't happen in practice
|
|
128
|
-
* since hooks.server.ts always sends the header).
|
|
129
|
-
*/
|
|
130
|
-
private getSubdomain;
|
|
131
|
-
/**
|
|
132
|
-
* Get the tenant ID (UUID) for this tenant
|
|
133
|
-
*
|
|
134
|
-
* Returns the cached ID from config, or null if not yet loaded.
|
|
135
|
-
* The ID is fetched from D1 on first config load and cached.
|
|
136
|
-
*/
|
|
137
|
-
private getTenantId;
|
|
138
|
-
}
|
|
139
|
-
interface Env {
|
|
140
|
-
DB: D1Database;
|
|
141
|
-
CACHE_KV: KVNamespace;
|
|
142
|
-
}
|
|
143
|
-
export {};
|