@tuturuuu/ai 0.0.10
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/README.md +76 -0
- package/package.json +106 -0
- package/src/api-key-hash.ts +28 -0
- package/src/calendar/events.ts +34 -0
- package/src/calendar/route.ts +114 -0
- package/src/chat/credit-source.ts +1 -0
- package/src/chat/google/chat-request-schema.ts +150 -0
- package/src/chat/google/default-system-instruction.ts +198 -0
- package/src/chat/google/message-file-processing.ts +212 -0
- package/src/chat/google/mira-step-preparation.ts +221 -0
- package/src/chat/google/new/route.ts +368 -0
- package/src/chat/google/route-auth.ts +81 -0
- package/src/chat/google/route-chat-resolution.ts +98 -0
- package/src/chat/google/route-credits.ts +61 -0
- package/src/chat/google/route-message-preparation.ts +331 -0
- package/src/chat/google/route-mira-runtime.ts +206 -0
- package/src/chat/google/route.ts +632 -0
- package/src/chat/google/stream-finish-persistence.ts +722 -0
- package/src/chat/google/summary/route.ts +153 -0
- package/src/chat/mira-render-ui-policy.ts +540 -0
- package/src/chat/mira-system-instruction.ts +484 -0
- package/src/chat-sdk/adapters.ts +389 -0
- package/src/chat-sdk/registry.ts +197 -0
- package/src/chat-sdk.ts +33 -0
- package/src/core.ts +3 -0
- package/src/credits/cap-output-tokens.ts +90 -0
- package/src/credits/check-credits.ts +232 -0
- package/src/credits/constants.ts +30 -0
- package/src/credits/index.ts +46 -0
- package/src/credits/model-mapping.ts +92 -0
- package/src/credits/reservations.ts +514 -0
- package/src/credits/resolve-plan-model.ts +219 -0
- package/src/credits/sync-gateway-models.ts +351 -0
- package/src/credits/types.ts +109 -0
- package/src/credits/use-ai-credits.ts +3 -0
- package/src/embeddings/metered.ts +283 -0
- package/src/executions/route.ts +137 -0
- package/src/generate/route.ts +411 -0
- package/src/hooks.ts +7 -0
- package/src/meetings/summary/route.ts +7 -0
- package/src/meetings/transcription/route.ts +134 -0
- package/src/memory/client.ts +158 -0
- package/src/memory/config.ts +38 -0
- package/src/memory/index.ts +32 -0
- package/src/memory/ingest.ts +51 -0
- package/src/memory/middleware.ts +35 -0
- package/src/memory/operations.ts +480 -0
- package/src/memory/scope.ts +102 -0
- package/src/memory/settings.ts +121 -0
- package/src/memory/types.ts +101 -0
- package/src/memory/workspace.ts +36 -0
- package/src/memory.ts +1 -0
- package/src/mind/patch.ts +146 -0
- package/src/mind/route.ts +687 -0
- package/src/mind/tools.ts +1500 -0
- package/src/mind/types.ts +20 -0
- package/src/object/core.ts +3 -0
- package/src/object/flashcards/route.ts +140 -0
- package/src/object/quizzes/explanation/route.ts +145 -0
- package/src/object/quizzes/route.ts +142 -0
- package/src/object/types.ts +187 -0
- package/src/object/year-plan/route.ts +196 -0
- package/src/react.ts +1 -0
- package/src/scheduling/algorithm.ts +791 -0
- package/src/scheduling/default.ts +36 -0
- package/src/scheduling/duration-optimizer.ts +689 -0
- package/src/scheduling/index.ts +79 -0
- package/src/scheduling/priority-calculator.ts +187 -0
- package/src/scheduling/recurrence-calculator.ts +621 -0
- package/src/scheduling/templates.ts +892 -0
- package/src/scheduling/types.ts +136 -0
- package/src/scheduling/web-adapter.ts +308 -0
- package/src/scheduling.ts +6 -0
- package/src/supported-actions.ts +1 -0
- package/src/supported-providers.ts +6 -0
- package/src/tools/context-builder.ts +372 -0
- package/src/tools/core.ts +1 -0
- package/src/tools/definitions/calendar.ts +106 -0
- package/src/tools/definitions/finance.ts +197 -0
- package/src/tools/definitions/image.ts +74 -0
- package/src/tools/definitions/memory.ts +83 -0
- package/src/tools/definitions/meta.ts +154 -0
- package/src/tools/definitions/render-ui.ts +81 -0
- package/src/tools/definitions/tasks.ts +343 -0
- package/src/tools/definitions/time-tracking.ts +381 -0
- package/src/tools/definitions/workspace-context.ts +45 -0
- package/src/tools/definitions/workspace-user-chat.ts +111 -0
- package/src/tools/executors/calendar.ts +371 -0
- package/src/tools/executors/chat.ts +15 -0
- package/src/tools/executors/finance.ts +638 -0
- package/src/tools/executors/helpers/encryption.ts +107 -0
- package/src/tools/executors/image.ts +247 -0
- package/src/tools/executors/markitdown.ts +684 -0
- package/src/tools/executors/memory.ts +277 -0
- package/src/tools/executors/parallel-checks.ts +176 -0
- package/src/tools/executors/qr.ts +170 -0
- package/src/tools/executors/scope-helpers.ts +192 -0
- package/src/tools/executors/search.ts +149 -0
- package/src/tools/executors/settings.ts +40 -0
- package/src/tools/executors/tasks.ts +1087 -0
- package/src/tools/executors/theme.ts +23 -0
- package/src/tools/executors/timer/timer-categories-executor.ts +110 -0
- package/src/tools/executors/timer/timer-category-mutations.ts +240 -0
- package/src/tools/executors/timer/timer-goal-mutations.ts +323 -0
- package/src/tools/executors/timer/timer-goals-executor.ts +272 -0
- package/src/tools/executors/timer/timer-helpers.ts +372 -0
- package/src/tools/executors/timer/timer-mutation-schemas.ts +160 -0
- package/src/tools/executors/timer/timer-mutation-types.ts +212 -0
- package/src/tools/executors/timer/timer-mutations.ts +19 -0
- package/src/tools/executors/timer/timer-queries.ts +18 -0
- package/src/tools/executors/timer/timer-session-lifecycle.ts +299 -0
- package/src/tools/executors/timer/timer-session-mutations.ts +10 -0
- package/src/tools/executors/timer/timer-session-queries.ts +153 -0
- package/src/tools/executors/timer/timer-session-updates.ts +200 -0
- package/src/tools/executors/timer/timer-sessions-executor.ts +91 -0
- package/src/tools/executors/timer/timer-stats-executor.ts +157 -0
- package/src/tools/executors/timer.ts +22 -0
- package/src/tools/executors/user.ts +60 -0
- package/src/tools/executors/workspace.ts +135 -0
- package/src/tools/json-render-catalog.ts +875 -0
- package/src/tools/mira-tool-definitions.ts +55 -0
- package/src/tools/mira-tool-dispatcher.ts +265 -0
- package/src/tools/mira-tool-metadata.ts +164 -0
- package/src/tools/mira-tool-names.ts +95 -0
- package/src/tools/mira-tool-render-ui.ts +54 -0
- package/src/tools/mira-tool-types.ts +17 -0
- package/src/tools/mira-tools.ts +167 -0
- package/src/tools/normalize-render-ui-input.ts +321 -0
- package/src/tools/workspace-context.ts +233 -0
- package/src/types.ts +38 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { MiraToolContext } from '../mira-tools';
|
|
2
|
+
|
|
3
|
+
export async function executeSetTheme(
|
|
4
|
+
args: Record<string, unknown>,
|
|
5
|
+
_ctx: MiraToolContext
|
|
6
|
+
) {
|
|
7
|
+
const theme = args.theme as string;
|
|
8
|
+
const valid = ['light', 'dark', 'system'];
|
|
9
|
+
if (!valid.includes(theme)) {
|
|
10
|
+
return {
|
|
11
|
+
error: `Invalid theme "${theme}". Must be one of: ${valid.join(', ')}`,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Return a client-side action marker — the chat UI will detect this
|
|
16
|
+
// and apply the theme change via next-themes.
|
|
17
|
+
return {
|
|
18
|
+
success: true,
|
|
19
|
+
action: 'set_theme',
|
|
20
|
+
theme,
|
|
21
|
+
message: `Theme changed to ${theme}`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { MiraToolContext } from '../../mira-tools';
|
|
2
|
+
import { getWorkspaceContextWorkspaceId } from '../../workspace-context';
|
|
3
|
+
import { buildToolFailure } from './timer-helpers';
|
|
4
|
+
|
|
5
|
+
const encodeCategoryCursorName = (value: string) => encodeURIComponent(value);
|
|
6
|
+
|
|
7
|
+
const decodeCategoryCursorName = (value: string) => {
|
|
8
|
+
try {
|
|
9
|
+
return decodeURIComponent(value);
|
|
10
|
+
} catch {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type TimeTrackingCategoryRow = {
|
|
16
|
+
id: string;
|
|
17
|
+
ws_id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
description: string | null;
|
|
20
|
+
color: string | null;
|
|
21
|
+
created_at: string;
|
|
22
|
+
updated_at: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export async function executeListTimeTrackingCategories(
|
|
26
|
+
args: Record<string, unknown>,
|
|
27
|
+
ctx: MiraToolContext
|
|
28
|
+
) {
|
|
29
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
30
|
+
const limitRaw = Number(args.limit);
|
|
31
|
+
const limit =
|
|
32
|
+
Number.isFinite(limitRaw) && limitRaw > 0
|
|
33
|
+
? Math.min(Math.floor(limitRaw), 50)
|
|
34
|
+
: 20;
|
|
35
|
+
const cursor = args.cursor;
|
|
36
|
+
|
|
37
|
+
let query = ctx.supabase
|
|
38
|
+
.from('time_tracking_categories')
|
|
39
|
+
.select('id, ws_id, name, description, color, created_at, updated_at')
|
|
40
|
+
.eq('ws_id', workspaceId)
|
|
41
|
+
.order('name', { ascending: true })
|
|
42
|
+
.order('id', { ascending: true })
|
|
43
|
+
.limit(limit + 1);
|
|
44
|
+
|
|
45
|
+
if (cursor !== undefined) {
|
|
46
|
+
if (typeof cursor !== 'string') {
|
|
47
|
+
return buildToolFailure(
|
|
48
|
+
'TT_CATEGORIES_INVALID_CURSOR',
|
|
49
|
+
'Invalid cursor format',
|
|
50
|
+
false
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const separatorIndex = cursor.lastIndexOf('|');
|
|
55
|
+
if (separatorIndex <= 0 || separatorIndex === cursor.length - 1) {
|
|
56
|
+
return buildToolFailure(
|
|
57
|
+
'TT_CATEGORIES_INVALID_CURSOR',
|
|
58
|
+
'Invalid cursor format',
|
|
59
|
+
false
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const rawName = cursor.slice(0, separatorIndex);
|
|
64
|
+
const lastId = cursor.slice(separatorIndex + 1);
|
|
65
|
+
const lastName = decodeCategoryCursorName(rawName);
|
|
66
|
+
if (!lastName || !lastId) {
|
|
67
|
+
return buildToolFailure(
|
|
68
|
+
'TT_CATEGORIES_INVALID_CURSOR',
|
|
69
|
+
'Invalid cursor format',
|
|
70
|
+
false
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const esc = (value: string) =>
|
|
75
|
+
value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
76
|
+
|
|
77
|
+
query = query.or(
|
|
78
|
+
`name.gt."${esc(lastName)}",and(name.eq."${esc(lastName)}",id.gt."${esc(lastId)}")`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { data, error } = await query;
|
|
83
|
+
if (error) {
|
|
84
|
+
return buildToolFailure('TT_CATEGORIES_FETCH_FAILED', error.message, true);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const rows = (data ?? []) as TimeTrackingCategoryRow[];
|
|
88
|
+
const hasMore = rows.length > limit;
|
|
89
|
+
const categories = hasMore ? rows.slice(0, limit) : rows;
|
|
90
|
+
const last = categories[categories.length - 1];
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
success: true,
|
|
94
|
+
categories,
|
|
95
|
+
count: categories.length,
|
|
96
|
+
hasMore,
|
|
97
|
+
nextCursor: last
|
|
98
|
+
? `${encodeCategoryCursorName(last.name)}|${last.id}`
|
|
99
|
+
: null,
|
|
100
|
+
meta: {
|
|
101
|
+
workspaceId,
|
|
102
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
103
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
104
|
+
filtersApplied: {
|
|
105
|
+
cursorProvided: cursor !== undefined,
|
|
106
|
+
limit,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { TablesUpdate } from '@tuturuuu/types';
|
|
2
|
+
import type { MiraToolContext } from '../../mira-tools';
|
|
3
|
+
import { getWorkspaceContextWorkspaceId } from '../../workspace-context';
|
|
4
|
+
import { buildToolFailure, coerceOptionalString } from './timer-helpers';
|
|
5
|
+
import {
|
|
6
|
+
type CreateTimeTrackingCategoryArgs,
|
|
7
|
+
createTimeTrackingCategoryArgsSchema,
|
|
8
|
+
type DeleteTimeTrackingCategoryArgs,
|
|
9
|
+
deleteTimeTrackingCategoryArgsSchema,
|
|
10
|
+
getZodErrorMessage,
|
|
11
|
+
type UpdateTimeTrackingCategoryArgs,
|
|
12
|
+
updateTimeTrackingCategoryArgsSchema,
|
|
13
|
+
} from './timer-mutation-schemas';
|
|
14
|
+
import type { TimeTrackingCategory } from './timer-mutation-types';
|
|
15
|
+
|
|
16
|
+
export async function executeCreateTimeTrackingCategory(
|
|
17
|
+
args: Record<string, unknown>,
|
|
18
|
+
ctx: MiraToolContext
|
|
19
|
+
) {
|
|
20
|
+
let parsedArgs: CreateTimeTrackingCategoryArgs;
|
|
21
|
+
try {
|
|
22
|
+
parsedArgs = createTimeTrackingCategoryArgsSchema.parse(args);
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return buildToolFailure(
|
|
25
|
+
'TT_CATEGORY_CREATE_INVALID_ARGS',
|
|
26
|
+
getZodErrorMessage(error),
|
|
27
|
+
false
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
32
|
+
const name = parsedArgs.name.trim();
|
|
33
|
+
if (!name) {
|
|
34
|
+
return buildToolFailure(
|
|
35
|
+
'TT_CATEGORY_CREATE_NAME_REQUIRED',
|
|
36
|
+
'name is required',
|
|
37
|
+
false
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { data, error } = await ctx.supabase
|
|
42
|
+
.from('time_tracking_categories')
|
|
43
|
+
.insert({
|
|
44
|
+
ws_id: workspaceId,
|
|
45
|
+
name,
|
|
46
|
+
description: coerceOptionalString(parsedArgs.description),
|
|
47
|
+
color: coerceOptionalString(parsedArgs.color) ?? 'BLUE',
|
|
48
|
+
created_at: new Date().toISOString(),
|
|
49
|
+
updated_at: new Date().toISOString(),
|
|
50
|
+
})
|
|
51
|
+
.select('id, ws_id, name, description, color, created_at, updated_at')
|
|
52
|
+
.single();
|
|
53
|
+
|
|
54
|
+
if (error) {
|
|
55
|
+
return buildToolFailure('TT_CATEGORY_CREATE_FAILED', error.message, true);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
success: true,
|
|
60
|
+
message: 'Time tracking category created',
|
|
61
|
+
category: data as TimeTrackingCategory,
|
|
62
|
+
meta: {
|
|
63
|
+
workspaceId,
|
|
64
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
65
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function executeUpdateTimeTrackingCategory(
|
|
71
|
+
args: Record<string, unknown>,
|
|
72
|
+
ctx: MiraToolContext
|
|
73
|
+
) {
|
|
74
|
+
let parsedArgs: UpdateTimeTrackingCategoryArgs;
|
|
75
|
+
try {
|
|
76
|
+
parsedArgs = updateTimeTrackingCategoryArgsSchema.parse(args);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return buildToolFailure(
|
|
79
|
+
'TT_CATEGORY_UPDATE_INVALID_ARGS',
|
|
80
|
+
getZodErrorMessage(error),
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const categoryId =
|
|
86
|
+
coerceOptionalString(parsedArgs.categoryId) ??
|
|
87
|
+
coerceOptionalString(parsedArgs.id);
|
|
88
|
+
if (!categoryId) {
|
|
89
|
+
return buildToolFailure(
|
|
90
|
+
'TT_CATEGORY_UPDATE_MISSING_ID',
|
|
91
|
+
'categoryId is required',
|
|
92
|
+
false
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
97
|
+
|
|
98
|
+
const { data: existingCategory, error: existingCategoryError } =
|
|
99
|
+
await ctx.supabase
|
|
100
|
+
.from('time_tracking_categories')
|
|
101
|
+
.select('id')
|
|
102
|
+
.eq('id', categoryId)
|
|
103
|
+
.eq('ws_id', workspaceId)
|
|
104
|
+
.maybeSingle();
|
|
105
|
+
|
|
106
|
+
if (existingCategoryError) {
|
|
107
|
+
return buildToolFailure(
|
|
108
|
+
'TT_CATEGORY_UPDATE_LOOKUP_FAILED',
|
|
109
|
+
existingCategoryError.message,
|
|
110
|
+
true
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (!existingCategory) {
|
|
115
|
+
return buildToolFailure(
|
|
116
|
+
'TT_CATEGORY_UPDATE_NOT_FOUND',
|
|
117
|
+
'Category not found',
|
|
118
|
+
false
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const updates: TablesUpdate<'time_tracking_categories'> = {
|
|
123
|
+
updated_at: new Date().toISOString(),
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (parsedArgs.name !== undefined) {
|
|
127
|
+
const trimmedName = parsedArgs.name?.trim();
|
|
128
|
+
if (!trimmedName) {
|
|
129
|
+
return buildToolFailure(
|
|
130
|
+
'TT_CATEGORY_UPDATE_INVALID_NAME',
|
|
131
|
+
'name cannot be empty',
|
|
132
|
+
false
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
updates.name = trimmedName;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (parsedArgs.description !== undefined) {
|
|
139
|
+
updates.description = coerceOptionalString(parsedArgs.description);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (parsedArgs.color !== undefined) {
|
|
143
|
+
updates.color = coerceOptionalString(parsedArgs.color);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (Object.keys(updates).length === 1) {
|
|
147
|
+
return {
|
|
148
|
+
success: true,
|
|
149
|
+
message: 'No fields to update',
|
|
150
|
+
meta: {
|
|
151
|
+
workspaceId,
|
|
152
|
+
workspaceContextId:
|
|
153
|
+
ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
154
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const { data, error } = await ctx.supabase
|
|
160
|
+
.from('time_tracking_categories')
|
|
161
|
+
.update(updates)
|
|
162
|
+
.eq('id', categoryId)
|
|
163
|
+
.eq('ws_id', workspaceId)
|
|
164
|
+
.select('id, ws_id, name, description, color, created_at, updated_at')
|
|
165
|
+
.single();
|
|
166
|
+
|
|
167
|
+
if (error) {
|
|
168
|
+
return buildToolFailure('TT_CATEGORY_UPDATE_FAILED', error.message, true);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
message: 'Time tracking category updated',
|
|
174
|
+
category: data as TimeTrackingCategory,
|
|
175
|
+
meta: {
|
|
176
|
+
workspaceId,
|
|
177
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
178
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function executeDeleteTimeTrackingCategory(
|
|
184
|
+
args: Record<string, unknown>,
|
|
185
|
+
ctx: MiraToolContext
|
|
186
|
+
) {
|
|
187
|
+
let parsedArgs: DeleteTimeTrackingCategoryArgs;
|
|
188
|
+
try {
|
|
189
|
+
parsedArgs = deleteTimeTrackingCategoryArgsSchema.parse(args);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
return buildToolFailure(
|
|
192
|
+
'TT_CATEGORY_DELETE_INVALID_ARGS',
|
|
193
|
+
getZodErrorMessage(error),
|
|
194
|
+
false
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const categoryId =
|
|
199
|
+
coerceOptionalString(parsedArgs.categoryId) ??
|
|
200
|
+
coerceOptionalString(parsedArgs.id);
|
|
201
|
+
if (!categoryId) {
|
|
202
|
+
return buildToolFailure(
|
|
203
|
+
'TT_CATEGORY_DELETE_MISSING_ID',
|
|
204
|
+
'categoryId is required',
|
|
205
|
+
false
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
210
|
+
const { data, error } = await ctx.supabase
|
|
211
|
+
.from('time_tracking_categories')
|
|
212
|
+
.delete()
|
|
213
|
+
.eq('id', categoryId)
|
|
214
|
+
.eq('ws_id', workspaceId)
|
|
215
|
+
.select('id')
|
|
216
|
+
.maybeSingle();
|
|
217
|
+
|
|
218
|
+
if (error) {
|
|
219
|
+
return buildToolFailure('TT_CATEGORY_DELETE_FAILED', error.message, true);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!data) {
|
|
223
|
+
return buildToolFailure(
|
|
224
|
+
'TT_CATEGORY_DELETE_NOT_FOUND',
|
|
225
|
+
'Category not found',
|
|
226
|
+
false
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
message: 'Time tracking category deleted',
|
|
233
|
+
deletedCategoryId: categoryId,
|
|
234
|
+
meta: {
|
|
235
|
+
workspaceId,
|
|
236
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
237
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import type { TablesUpdate } from '@tuturuuu/types';
|
|
2
|
+
import type { MiraToolContext } from '../../mira-tools';
|
|
3
|
+
import { getWorkspaceContextWorkspaceId } from '../../workspace-context';
|
|
4
|
+
import { buildToolFailure, coerceOptionalString } from './timer-helpers';
|
|
5
|
+
import {
|
|
6
|
+
type CreateTimeTrackerGoalArgs,
|
|
7
|
+
createTimeTrackerGoalArgsSchema,
|
|
8
|
+
type DeleteTimeTrackerGoalArgs,
|
|
9
|
+
deleteTimeTrackerGoalArgsSchema,
|
|
10
|
+
getZodErrorMessage,
|
|
11
|
+
type UpdateTimeTrackerGoalArgs,
|
|
12
|
+
updateTimeTrackerGoalArgsSchema,
|
|
13
|
+
} from './timer-mutation-schemas';
|
|
14
|
+
import {
|
|
15
|
+
normalizeGoalCategory,
|
|
16
|
+
normalizeGoalCategoryIdInput,
|
|
17
|
+
type TimerGoal,
|
|
18
|
+
} from './timer-mutation-types';
|
|
19
|
+
|
|
20
|
+
export async function executeCreateTimeTrackerGoal(
|
|
21
|
+
args: Record<string, unknown>,
|
|
22
|
+
ctx: MiraToolContext
|
|
23
|
+
) {
|
|
24
|
+
let parsedArgs: CreateTimeTrackerGoalArgs;
|
|
25
|
+
try {
|
|
26
|
+
parsedArgs = createTimeTrackerGoalArgsSchema.parse(args);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return buildToolFailure(
|
|
29
|
+
'TT_GOAL_CREATE_INVALID_ARGS',
|
|
30
|
+
getZodErrorMessage(error),
|
|
31
|
+
false
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
36
|
+
const normalizedCategory = normalizeGoalCategoryIdInput(
|
|
37
|
+
parsedArgs.categoryId
|
|
38
|
+
);
|
|
39
|
+
if (!normalizedCategory.ok) {
|
|
40
|
+
return buildToolFailure(
|
|
41
|
+
'TT_GOAL_CREATE_INVALID_CATEGORY',
|
|
42
|
+
normalizedCategory.error,
|
|
43
|
+
false
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (normalizedCategory.categoryId) {
|
|
48
|
+
const { data: categoryRow, error: categoryError } = await ctx.supabase
|
|
49
|
+
.from('time_tracking_categories')
|
|
50
|
+
.select('id')
|
|
51
|
+
.eq('id', normalizedCategory.categoryId)
|
|
52
|
+
.eq('ws_id', workspaceId)
|
|
53
|
+
.maybeSingle();
|
|
54
|
+
|
|
55
|
+
if (categoryError) {
|
|
56
|
+
return buildToolFailure(
|
|
57
|
+
'TT_GOAL_CREATE_CATEGORY_LOOKUP_FAILED',
|
|
58
|
+
categoryError.message,
|
|
59
|
+
true
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!categoryRow) {
|
|
64
|
+
return buildToolFailure(
|
|
65
|
+
'TT_GOAL_CREATE_CATEGORY_NOT_FOUND',
|
|
66
|
+
'Category not found',
|
|
67
|
+
false
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { data, error } = await ctx.supabase
|
|
73
|
+
.from('time_tracking_goals')
|
|
74
|
+
.insert({
|
|
75
|
+
ws_id: workspaceId,
|
|
76
|
+
user_id: ctx.userId,
|
|
77
|
+
category_id: normalizedCategory.categoryId,
|
|
78
|
+
daily_goal_minutes: parsedArgs.dailyGoalMinutes,
|
|
79
|
+
weekly_goal_minutes: parsedArgs.weeklyGoalMinutes ?? null,
|
|
80
|
+
is_active: parsedArgs.isActive ?? true,
|
|
81
|
+
created_at: new Date().toISOString(),
|
|
82
|
+
updated_at: new Date().toISOString(),
|
|
83
|
+
})
|
|
84
|
+
.select(
|
|
85
|
+
`
|
|
86
|
+
*,
|
|
87
|
+
category:time_tracking_categories(id, name, color)
|
|
88
|
+
`
|
|
89
|
+
)
|
|
90
|
+
.single();
|
|
91
|
+
|
|
92
|
+
if (error) {
|
|
93
|
+
return buildToolFailure('TT_GOAL_CREATE_FAILED', error.message, true);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const goal = data as TimerGoal;
|
|
97
|
+
return {
|
|
98
|
+
success: true,
|
|
99
|
+
message: 'Time tracker goal created',
|
|
100
|
+
goal: {
|
|
101
|
+
...goal,
|
|
102
|
+
category: normalizeGoalCategory(goal.category),
|
|
103
|
+
},
|
|
104
|
+
meta: {
|
|
105
|
+
workspaceId,
|
|
106
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
107
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function executeUpdateTimeTrackerGoal(
|
|
113
|
+
args: Record<string, unknown>,
|
|
114
|
+
ctx: MiraToolContext
|
|
115
|
+
) {
|
|
116
|
+
let parsedArgs: UpdateTimeTrackerGoalArgs;
|
|
117
|
+
try {
|
|
118
|
+
parsedArgs = updateTimeTrackerGoalArgsSchema.parse(args);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return buildToolFailure(
|
|
121
|
+
'TT_GOAL_UPDATE_INVALID_ARGS',
|
|
122
|
+
getZodErrorMessage(error),
|
|
123
|
+
false
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const goalId =
|
|
128
|
+
coerceOptionalString(parsedArgs.goalId) ??
|
|
129
|
+
coerceOptionalString(parsedArgs.id);
|
|
130
|
+
if (!goalId) {
|
|
131
|
+
return buildToolFailure(
|
|
132
|
+
'TT_GOAL_UPDATE_MISSING_ID',
|
|
133
|
+
'goalId is required',
|
|
134
|
+
false
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
139
|
+
|
|
140
|
+
const { data: existingGoal, error: existingGoalError } = await ctx.supabase
|
|
141
|
+
.from('time_tracking_goals')
|
|
142
|
+
.select('id')
|
|
143
|
+
.eq('id', goalId)
|
|
144
|
+
.eq('ws_id', workspaceId)
|
|
145
|
+
.eq('user_id', ctx.userId)
|
|
146
|
+
.maybeSingle();
|
|
147
|
+
|
|
148
|
+
if (existingGoalError) {
|
|
149
|
+
return buildToolFailure(
|
|
150
|
+
'TT_GOAL_UPDATE_LOOKUP_FAILED',
|
|
151
|
+
existingGoalError.message,
|
|
152
|
+
true
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!existingGoal) {
|
|
157
|
+
return buildToolFailure(
|
|
158
|
+
'TT_GOAL_UPDATE_NOT_FOUND',
|
|
159
|
+
'Goal not found',
|
|
160
|
+
false
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const updates: TablesUpdate<'time_tracking_goals'> = {
|
|
165
|
+
updated_at: new Date().toISOString(),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (parsedArgs.categoryId !== undefined) {
|
|
169
|
+
const normalizedCategory = normalizeGoalCategoryIdInput(
|
|
170
|
+
parsedArgs.categoryId
|
|
171
|
+
);
|
|
172
|
+
if (!normalizedCategory.ok) {
|
|
173
|
+
return buildToolFailure(
|
|
174
|
+
'TT_GOAL_UPDATE_INVALID_CATEGORY',
|
|
175
|
+
normalizedCategory.error,
|
|
176
|
+
false
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (normalizedCategory.categoryId) {
|
|
181
|
+
const { data: categoryRow, error: categoryError } = await ctx.supabase
|
|
182
|
+
.from('time_tracking_categories')
|
|
183
|
+
.select('id')
|
|
184
|
+
.eq('id', normalizedCategory.categoryId)
|
|
185
|
+
.eq('ws_id', workspaceId)
|
|
186
|
+
.maybeSingle();
|
|
187
|
+
|
|
188
|
+
if (categoryError) {
|
|
189
|
+
return buildToolFailure(
|
|
190
|
+
'TT_GOAL_UPDATE_CATEGORY_LOOKUP_FAILED',
|
|
191
|
+
categoryError.message,
|
|
192
|
+
true
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (!categoryRow) {
|
|
197
|
+
return buildToolFailure(
|
|
198
|
+
'TT_GOAL_UPDATE_CATEGORY_NOT_FOUND',
|
|
199
|
+
'Category not found',
|
|
200
|
+
false
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
updates.category_id = normalizedCategory.categoryId;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (parsedArgs.dailyGoalMinutes !== undefined) {
|
|
209
|
+
updates.daily_goal_minutes = parsedArgs.dailyGoalMinutes;
|
|
210
|
+
}
|
|
211
|
+
if (parsedArgs.weeklyGoalMinutes !== undefined) {
|
|
212
|
+
updates.weekly_goal_minutes = parsedArgs.weeklyGoalMinutes ?? null;
|
|
213
|
+
}
|
|
214
|
+
if (parsedArgs.isActive !== undefined) {
|
|
215
|
+
updates.is_active = parsedArgs.isActive;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (Object.keys(updates).length === 1) {
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
message: 'No fields to update',
|
|
222
|
+
meta: {
|
|
223
|
+
workspaceId,
|
|
224
|
+
workspaceContextId:
|
|
225
|
+
ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
226
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const { data, error } = await ctx.supabase
|
|
232
|
+
.from('time_tracking_goals')
|
|
233
|
+
.update(updates)
|
|
234
|
+
.eq('id', goalId)
|
|
235
|
+
.eq('ws_id', workspaceId)
|
|
236
|
+
.eq('user_id', ctx.userId)
|
|
237
|
+
.select(
|
|
238
|
+
`
|
|
239
|
+
*,
|
|
240
|
+
category:time_tracking_categories(id, name, color)
|
|
241
|
+
`
|
|
242
|
+
)
|
|
243
|
+
.single();
|
|
244
|
+
|
|
245
|
+
if (error) {
|
|
246
|
+
return buildToolFailure('TT_GOAL_UPDATE_FAILED', error.message, true);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const goal = data as TimerGoal;
|
|
250
|
+
return {
|
|
251
|
+
success: true,
|
|
252
|
+
message: 'Time tracker goal updated',
|
|
253
|
+
goal: {
|
|
254
|
+
...goal,
|
|
255
|
+
category: normalizeGoalCategory(goal.category),
|
|
256
|
+
},
|
|
257
|
+
meta: {
|
|
258
|
+
workspaceId,
|
|
259
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
260
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export async function executeDeleteTimeTrackerGoal(
|
|
266
|
+
args: Record<string, unknown>,
|
|
267
|
+
ctx: MiraToolContext
|
|
268
|
+
) {
|
|
269
|
+
let parsedArgs: DeleteTimeTrackerGoalArgs;
|
|
270
|
+
try {
|
|
271
|
+
parsedArgs = deleteTimeTrackerGoalArgsSchema.parse(args);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return buildToolFailure(
|
|
274
|
+
'TT_GOAL_DELETE_INVALID_ARGS',
|
|
275
|
+
getZodErrorMessage(error),
|
|
276
|
+
false
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const goalId =
|
|
281
|
+
coerceOptionalString(parsedArgs.goalId) ??
|
|
282
|
+
coerceOptionalString(parsedArgs.id);
|
|
283
|
+
if (!goalId) {
|
|
284
|
+
return buildToolFailure(
|
|
285
|
+
'TT_GOAL_DELETE_MISSING_ID',
|
|
286
|
+
'goalId is required',
|
|
287
|
+
false
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const workspaceId = getWorkspaceContextWorkspaceId(ctx);
|
|
292
|
+
const { data, error } = await ctx.supabase
|
|
293
|
+
.from('time_tracking_goals')
|
|
294
|
+
.delete()
|
|
295
|
+
.eq('id', goalId)
|
|
296
|
+
.eq('ws_id', workspaceId)
|
|
297
|
+
.eq('user_id', ctx.userId)
|
|
298
|
+
.select('id')
|
|
299
|
+
.maybeSingle();
|
|
300
|
+
|
|
301
|
+
if (error) {
|
|
302
|
+
return buildToolFailure('TT_GOAL_DELETE_FAILED', error.message, true);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!data) {
|
|
306
|
+
return buildToolFailure(
|
|
307
|
+
'TT_GOAL_DELETE_NOT_FOUND',
|
|
308
|
+
'Goal not found',
|
|
309
|
+
false
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
success: true,
|
|
315
|
+
message: 'Time tracker goal deleted',
|
|
316
|
+
deletedGoalId: goalId,
|
|
317
|
+
meta: {
|
|
318
|
+
workspaceId,
|
|
319
|
+
workspaceContextId: ctx.workspaceContext?.workspaceContextId ?? ctx.wsId,
|
|
320
|
+
isPersonalContext: ctx.workspaceContext?.personal ?? false,
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|