decibel-tools-mcp 1.0.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/LICENSE +21 -0
- package/README.md +335 -0
- package/dist/agentic/compiler.d.ts +21 -0
- package/dist/agentic/compiler.d.ts.map +1 -0
- package/dist/agentic/compiler.js +267 -0
- package/dist/agentic/compiler.js.map +1 -0
- package/dist/agentic/golden.d.ts +25 -0
- package/dist/agentic/golden.d.ts.map +1 -0
- package/dist/agentic/golden.js +255 -0
- package/dist/agentic/golden.js.map +1 -0
- package/dist/agentic/index.d.ts +17 -0
- package/dist/agentic/index.d.ts.map +1 -0
- package/dist/agentic/index.js +153 -0
- package/dist/agentic/index.js.map +1 -0
- package/dist/agentic/linter.d.ts +20 -0
- package/dist/agentic/linter.d.ts.map +1 -0
- package/dist/agentic/linter.js +340 -0
- package/dist/agentic/linter.js.map +1 -0
- package/dist/agentic/renderer.d.ts +17 -0
- package/dist/agentic/renderer.d.ts.map +1 -0
- package/dist/agentic/renderer.js +277 -0
- package/dist/agentic/renderer.js.map +1 -0
- package/dist/agentic/types.d.ts +199 -0
- package/dist/agentic/types.d.ts.map +1 -0
- package/dist/agentic/types.js +8 -0
- package/dist/agentic/types.js.map +1 -0
- package/dist/architectAdrs.d.ts +19 -0
- package/dist/architectAdrs.d.ts.map +1 -0
- package/dist/architectAdrs.js +123 -0
- package/dist/architectAdrs.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -0
- package/dist/dataRoot.d.ts +45 -0
- package/dist/dataRoot.d.ts.map +1 -0
- package/dist/dataRoot.js +201 -0
- package/dist/dataRoot.js.map +1 -0
- package/dist/decibelPaths.d.ts +42 -0
- package/dist/decibelPaths.d.ts.map +1 -0
- package/dist/decibelPaths.js +150 -0
- package/dist/decibelPaths.js.map +1 -0
- package/dist/httpServer.d.ts +49 -0
- package/dist/httpServer.d.ts.map +1 -0
- package/dist/httpServer.js +1472 -0
- package/dist/httpServer.js.map +1 -0
- package/dist/lib/benchmark.d.ts +110 -0
- package/dist/lib/benchmark.d.ts.map +1 -0
- package/dist/lib/benchmark.js +338 -0
- package/dist/lib/benchmark.js.map +1 -0
- package/dist/lib/supabase.d.ts +123 -0
- package/dist/lib/supabase.d.ts.map +1 -0
- package/dist/lib/supabase.js +91 -0
- package/dist/lib/supabase.js.map +1 -0
- package/dist/projectPaths.d.ts +27 -0
- package/dist/projectPaths.d.ts.map +1 -0
- package/dist/projectPaths.js +86 -0
- package/dist/projectPaths.js.map +1 -0
- package/dist/projectRegistry.d.ts +97 -0
- package/dist/projectRegistry.d.ts.map +1 -0
- package/dist/projectRegistry.js +362 -0
- package/dist/projectRegistry.js.map +1 -0
- package/dist/sentinelIssues.d.ts +44 -0
- package/dist/sentinelIssues.d.ts.map +1 -0
- package/dist/sentinelIssues.js +213 -0
- package/dist/sentinelIssues.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +93 -0
- package/dist/server.js.map +1 -0
- package/dist/test.d.ts +7 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +77 -0
- package/dist/test.js.map +1 -0
- package/dist/tools/agentic/index.d.ts +7 -0
- package/dist/tools/agentic/index.d.ts.map +1 -0
- package/dist/tools/agentic/index.js +180 -0
- package/dist/tools/agentic/index.js.map +1 -0
- package/dist/tools/architect/index.d.ts +9 -0
- package/dist/tools/architect/index.d.ts.map +1 -0
- package/dist/tools/architect/index.js +383 -0
- package/dist/tools/architect/index.js.map +1 -0
- package/dist/tools/architect.d.ts +19 -0
- package/dist/tools/architect.d.ts.map +1 -0
- package/dist/tools/architect.js +88 -0
- package/dist/tools/architect.js.map +1 -0
- package/dist/tools/bench.d.ts +89 -0
- package/dist/tools/bench.d.ts.map +1 -0
- package/dist/tools/bench.js +826 -0
- package/dist/tools/bench.js.map +1 -0
- package/dist/tools/context/index.d.ts +11 -0
- package/dist/tools/context/index.d.ts.map +1 -0
- package/dist/tools/context/index.js +439 -0
- package/dist/tools/context/index.js.map +1 -0
- package/dist/tools/context.d.ts +146 -0
- package/dist/tools/context.d.ts.map +1 -0
- package/dist/tools/context.js +481 -0
- package/dist/tools/context.js.map +1 -0
- package/dist/tools/crit.d.ts +63 -0
- package/dist/tools/crit.d.ts.map +1 -0
- package/dist/tools/crit.js +159 -0
- package/dist/tools/crit.js.map +1 -0
- package/dist/tools/data-inspector.d.ts +188 -0
- package/dist/tools/data-inspector.d.ts.map +1 -0
- package/dist/tools/data-inspector.js +650 -0
- package/dist/tools/data-inspector.js.map +1 -0
- package/dist/tools/deck.d.ts +11 -0
- package/dist/tools/deck.d.ts.map +1 -0
- package/dist/tools/deck.js +170 -0
- package/dist/tools/deck.js.map +1 -0
- package/dist/tools/designer/index.d.ts +6 -0
- package/dist/tools/designer/index.d.ts.map +1 -0
- package/dist/tools/designer/index.js +177 -0
- package/dist/tools/designer/index.js.map +1 -0
- package/dist/tools/designer.d.ts +20 -0
- package/dist/tools/designer.d.ts.map +1 -0
- package/dist/tools/designer.js +75 -0
- package/dist/tools/designer.js.map +1 -0
- package/dist/tools/dojo/index.d.ts +13 -0
- package/dist/tools/dojo/index.d.ts.map +1 -0
- package/dist/tools/dojo/index.js +547 -0
- package/dist/tools/dojo/index.js.map +1 -0
- package/dist/tools/dojo.d.ts +254 -0
- package/dist/tools/dojo.d.ts.map +1 -0
- package/dist/tools/dojo.js +933 -0
- package/dist/tools/dojo.js.map +1 -0
- package/dist/tools/dojoBench.d.ts +49 -0
- package/dist/tools/dojoBench.d.ts.map +1 -0
- package/dist/tools/dojoBench.js +205 -0
- package/dist/tools/dojoBench.js.map +1 -0
- package/dist/tools/dojoGraduated.d.ts +50 -0
- package/dist/tools/dojoGraduated.d.ts.map +1 -0
- package/dist/tools/dojoGraduated.js +174 -0
- package/dist/tools/dojoGraduated.js.map +1 -0
- package/dist/tools/dojoPolicy.d.ts +65 -0
- package/dist/tools/dojoPolicy.d.ts.map +1 -0
- package/dist/tools/dojoPolicy.js +263 -0
- package/dist/tools/dojoPolicy.js.map +1 -0
- package/dist/tools/friction/index.d.ts +7 -0
- package/dist/tools/friction/index.d.ts.map +1 -0
- package/dist/tools/friction/index.js +239 -0
- package/dist/tools/friction/index.js.map +1 -0
- package/dist/tools/friction.d.ts +82 -0
- package/dist/tools/friction.d.ts.map +1 -0
- package/dist/tools/friction.js +331 -0
- package/dist/tools/friction.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/learnings/index.d.ts +5 -0
- package/dist/tools/learnings/index.d.ts.map +1 -0
- package/dist/tools/learnings/index.js +123 -0
- package/dist/tools/learnings/index.js.map +1 -0
- package/dist/tools/learnings.d.ts +41 -0
- package/dist/tools/learnings.d.ts.map +1 -0
- package/dist/tools/learnings.js +149 -0
- package/dist/tools/learnings.js.map +1 -0
- package/dist/tools/oracle/index.d.ts +5 -0
- package/dist/tools/oracle/index.d.ts.map +1 -0
- package/dist/tools/oracle/index.js +97 -0
- package/dist/tools/oracle/index.js.map +1 -0
- package/dist/tools/oracle.d.ts +90 -0
- package/dist/tools/oracle.d.ts.map +1 -0
- package/dist/tools/oracle.js +529 -0
- package/dist/tools/oracle.js.map +1 -0
- package/dist/tools/policy.d.ts +119 -0
- package/dist/tools/policy.d.ts.map +1 -0
- package/dist/tools/policy.js +406 -0
- package/dist/tools/policy.js.map +1 -0
- package/dist/tools/provenance/index.d.ts +4 -0
- package/dist/tools/provenance/index.d.ts.map +1 -0
- package/dist/tools/provenance/index.js +58 -0
- package/dist/tools/provenance/index.js.map +1 -0
- package/dist/tools/provenance.d.ts +75 -0
- package/dist/tools/provenance.d.ts.map +1 -0
- package/dist/tools/provenance.js +197 -0
- package/dist/tools/provenance.js.map +1 -0
- package/dist/tools/rateLimiter.d.ts +45 -0
- package/dist/tools/rateLimiter.d.ts.map +1 -0
- package/dist/tools/rateLimiter.js +91 -0
- package/dist/tools/rateLimiter.js.map +1 -0
- package/dist/tools/registry/index.d.ts +10 -0
- package/dist/tools/registry/index.d.ts.map +1 -0
- package/dist/tools/registry/index.js +467 -0
- package/dist/tools/registry/index.js.map +1 -0
- package/dist/tools/registry.d.ts +3 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +189 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/roadmap/index.d.ts +9 -0
- package/dist/tools/roadmap/index.d.ts.map +1 -0
- package/dist/tools/roadmap/index.js +250 -0
- package/dist/tools/roadmap/index.js.map +1 -0
- package/dist/tools/roadmap.d.ts +88 -0
- package/dist/tools/roadmap.d.ts.map +1 -0
- package/dist/tools/roadmap.js +365 -0
- package/dist/tools/roadmap.js.map +1 -0
- package/dist/tools/sentinel/index.d.ts +19 -0
- package/dist/tools/sentinel/index.d.ts.map +1 -0
- package/dist/tools/sentinel/index.js +832 -0
- package/dist/tools/sentinel/index.js.map +1 -0
- package/dist/tools/sentinel-scan-data.d.ts +90 -0
- package/dist/tools/sentinel-scan-data.d.ts.map +1 -0
- package/dist/tools/sentinel-scan-data.js +122 -0
- package/dist/tools/sentinel-scan-data.js.map +1 -0
- package/dist/tools/sentinel.d.ts +156 -0
- package/dist/tools/sentinel.d.ts.map +1 -0
- package/dist/tools/sentinel.js +603 -0
- package/dist/tools/sentinel.js.map +1 -0
- package/dist/tools/shared/index.d.ts +4 -0
- package/dist/tools/shared/index.d.ts.map +1 -0
- package/dist/tools/shared/index.js +7 -0
- package/dist/tools/shared/index.js.map +1 -0
- package/dist/tools/shared/project.d.ts +17 -0
- package/dist/tools/shared/project.d.ts.map +1 -0
- package/dist/tools/shared/project.js +36 -0
- package/dist/tools/shared/project.js.map +1 -0
- package/dist/tools/shared/response.d.ts +10 -0
- package/dist/tools/shared/response.d.ts.map +1 -0
- package/dist/tools/shared/response.js +36 -0
- package/dist/tools/shared/response.js.map +1 -0
- package/dist/tools/shared/validation.d.ts +10 -0
- package/dist/tools/shared/validation.d.ts.map +1 -0
- package/dist/tools/shared/validation.js +26 -0
- package/dist/tools/shared/validation.js.map +1 -0
- package/dist/tools/studio/cloud-spine.d.ts +27 -0
- package/dist/tools/studio/cloud-spine.d.ts.map +1 -0
- package/dist/tools/studio/cloud-spine.js +773 -0
- package/dist/tools/studio/cloud-spine.js.map +1 -0
- package/dist/tools/studio/index.d.ts +154 -0
- package/dist/tools/studio/index.d.ts.map +1 -0
- package/dist/tools/studio/index.js +525 -0
- package/dist/tools/studio/index.js.map +1 -0
- package/dist/tools/testSpec.d.ts +122 -0
- package/dist/tools/testSpec.d.ts.map +1 -0
- package/dist/tools/testSpec.js +525 -0
- package/dist/tools/testSpec.js.map +1 -0
- package/dist/tools/toolsIndex.d.ts +5 -0
- package/dist/tools/toolsIndex.d.ts.map +1 -0
- package/dist/tools/toolsIndex.js +37 -0
- package/dist/tools/toolsIndex.js.map +1 -0
- package/dist/tools/types.d.ts +30 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +7 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/voice/index.d.ts +8 -0
- package/dist/tools/voice/index.d.ts.map +1 -0
- package/dist/tools/voice/index.js +176 -0
- package/dist/tools/voice/index.js.map +1 -0
- package/dist/tools/voice.d.ts +291 -0
- package/dist/tools/voice.d.ts.map +1 -0
- package/dist/tools/voice.js +734 -0
- package/dist/tools/voice.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio Cloud Spine Tools
|
|
3
|
+
*
|
|
4
|
+
* Supabase-backed tools for Decibel Studio cloud sync.
|
|
5
|
+
* Integrates with senken.pro Supabase for:
|
|
6
|
+
* - Project management
|
|
7
|
+
* - Artifact sync
|
|
8
|
+
* - Event streaming
|
|
9
|
+
* - Device registration
|
|
10
|
+
*
|
|
11
|
+
* EPIC-0002: Cloud Spine (Supabase Backend)
|
|
12
|
+
*/
|
|
13
|
+
import { toolSuccess, toolError, requireFields } from '../shared/index.js';
|
|
14
|
+
import { getSupabaseClient, isSupabaseConfigured, formatSupabaseError, } from '../../lib/supabase.js';
|
|
15
|
+
import { log } from '../../config.js';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Project Tools
|
|
18
|
+
// ============================================================================
|
|
19
|
+
export const studioListProjectsTool = {
|
|
20
|
+
definition: {
|
|
21
|
+
name: 'studio_list_projects',
|
|
22
|
+
description: 'List all Decibel Studio projects for the authenticated user.',
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: {
|
|
26
|
+
include_deleted: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
description: 'Include soft-deleted projects (default: false)',
|
|
29
|
+
},
|
|
30
|
+
limit: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
description: 'Maximum number of projects to return (default: 50)',
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
handler: async (args) => {
|
|
38
|
+
try {
|
|
39
|
+
if (!isSupabaseConfigured()) {
|
|
40
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
41
|
+
}
|
|
42
|
+
const client = getSupabaseClient();
|
|
43
|
+
const includeDeleted = args.include_deleted ?? false;
|
|
44
|
+
const limit = args.limit ?? 50;
|
|
45
|
+
log(`[Studio] Listing projects (includeDeleted=${includeDeleted}, limit=${limit})`);
|
|
46
|
+
let query = client
|
|
47
|
+
.from('projects')
|
|
48
|
+
.select('*')
|
|
49
|
+
.order('updated_at', { ascending: false })
|
|
50
|
+
.limit(limit);
|
|
51
|
+
if (!includeDeleted) {
|
|
52
|
+
query = query.is('deleted_at', null);
|
|
53
|
+
}
|
|
54
|
+
const { data, error } = await query;
|
|
55
|
+
if (error) {
|
|
56
|
+
return toolError(formatSupabaseError(error).error);
|
|
57
|
+
}
|
|
58
|
+
return toolSuccess({
|
|
59
|
+
projects: data,
|
|
60
|
+
count: data?.length ?? 0,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
export const studioCreateProjectTool = {
|
|
69
|
+
definition: {
|
|
70
|
+
name: 'studio_create_project',
|
|
71
|
+
description: 'Create a new Decibel Studio project.',
|
|
72
|
+
inputSchema: {
|
|
73
|
+
type: 'object',
|
|
74
|
+
properties: {
|
|
75
|
+
name: {
|
|
76
|
+
type: 'string',
|
|
77
|
+
description: 'Project name',
|
|
78
|
+
},
|
|
79
|
+
description: {
|
|
80
|
+
type: 'string',
|
|
81
|
+
description: 'Optional project description',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: ['name'],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
handler: async (args) => {
|
|
88
|
+
try {
|
|
89
|
+
requireFields(args, 'name');
|
|
90
|
+
if (!isSupabaseConfigured()) {
|
|
91
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
92
|
+
}
|
|
93
|
+
const client = getSupabaseClient();
|
|
94
|
+
// Get current user
|
|
95
|
+
const { data: { user }, error: authError } = await client.auth.getUser();
|
|
96
|
+
if (authError || !user) {
|
|
97
|
+
return toolError('Not authenticated. Please sign in first.');
|
|
98
|
+
}
|
|
99
|
+
log(`[Studio] Creating project: ${args.name}`);
|
|
100
|
+
const insert = {
|
|
101
|
+
owner_id: user.id,
|
|
102
|
+
name: args.name,
|
|
103
|
+
description: args.description ?? null,
|
|
104
|
+
};
|
|
105
|
+
const { data, error } = await client
|
|
106
|
+
.from('projects')
|
|
107
|
+
.insert(insert)
|
|
108
|
+
.select()
|
|
109
|
+
.single();
|
|
110
|
+
if (error) {
|
|
111
|
+
return toolError(formatSupabaseError(error).error);
|
|
112
|
+
}
|
|
113
|
+
return toolSuccess({
|
|
114
|
+
project: data,
|
|
115
|
+
message: `Project "${args.name}" created successfully`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
export const studioGetProjectTool = {
|
|
124
|
+
definition: {
|
|
125
|
+
name: 'studio_get_project',
|
|
126
|
+
description: 'Get details of a specific Decibel Studio project.',
|
|
127
|
+
inputSchema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
project_id: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: 'Project UUID',
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
required: ['project_id'],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
handler: async (args) => {
|
|
139
|
+
try {
|
|
140
|
+
requireFields(args, 'project_id');
|
|
141
|
+
if (!isSupabaseConfigured()) {
|
|
142
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
143
|
+
}
|
|
144
|
+
const client = getSupabaseClient();
|
|
145
|
+
const { data, error } = await client
|
|
146
|
+
.from('projects')
|
|
147
|
+
.select('*')
|
|
148
|
+
.eq('id', args.project_id)
|
|
149
|
+
.single();
|
|
150
|
+
if (error) {
|
|
151
|
+
return toolError(formatSupabaseError(error).error);
|
|
152
|
+
}
|
|
153
|
+
return toolSuccess({ project: data });
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// Artifact Tools
|
|
162
|
+
// ============================================================================
|
|
163
|
+
export const studioListArtifactsTool = {
|
|
164
|
+
definition: {
|
|
165
|
+
name: 'studio_list_artifacts',
|
|
166
|
+
description: 'List artifacts in a Decibel Studio project.',
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: 'object',
|
|
169
|
+
properties: {
|
|
170
|
+
project_id: {
|
|
171
|
+
type: 'string',
|
|
172
|
+
description: 'Project UUID',
|
|
173
|
+
},
|
|
174
|
+
type: {
|
|
175
|
+
type: 'string',
|
|
176
|
+
enum: ['image', 'video', '3d', 'audio', 'text'],
|
|
177
|
+
description: 'Filter by artifact type',
|
|
178
|
+
},
|
|
179
|
+
status: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
enum: ['generating', 'partial', 'complete', 'failed'],
|
|
182
|
+
description: 'Filter by status',
|
|
183
|
+
},
|
|
184
|
+
is_pinned: {
|
|
185
|
+
type: 'boolean',
|
|
186
|
+
description: 'Filter by pinned status',
|
|
187
|
+
},
|
|
188
|
+
limit: {
|
|
189
|
+
type: 'number',
|
|
190
|
+
description: 'Maximum number of artifacts to return (default: 50)',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
required: ['project_id'],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
handler: async (args) => {
|
|
197
|
+
try {
|
|
198
|
+
requireFields(args, 'project_id');
|
|
199
|
+
if (!isSupabaseConfigured()) {
|
|
200
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
201
|
+
}
|
|
202
|
+
const client = getSupabaseClient();
|
|
203
|
+
const limit = args.limit ?? 50;
|
|
204
|
+
log(`[Studio] Listing artifacts for project ${args.project_id}`);
|
|
205
|
+
let query = client
|
|
206
|
+
.from('artifacts')
|
|
207
|
+
.select('*')
|
|
208
|
+
.eq('project_id', args.project_id)
|
|
209
|
+
.is('deleted_at', null)
|
|
210
|
+
.order('created_at', { ascending: false })
|
|
211
|
+
.limit(limit);
|
|
212
|
+
if (args.type) {
|
|
213
|
+
query = query.eq('type', args.type);
|
|
214
|
+
}
|
|
215
|
+
if (args.status) {
|
|
216
|
+
query = query.eq('status', args.status);
|
|
217
|
+
}
|
|
218
|
+
if (args.is_pinned !== undefined) {
|
|
219
|
+
query = query.eq('is_pinned', args.is_pinned);
|
|
220
|
+
}
|
|
221
|
+
const { data, error } = await query;
|
|
222
|
+
if (error) {
|
|
223
|
+
return toolError(formatSupabaseError(error).error);
|
|
224
|
+
}
|
|
225
|
+
return toolSuccess({
|
|
226
|
+
artifacts: data,
|
|
227
|
+
count: data?.length ?? 0,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
export const studioCreateArtifactTool = {
|
|
236
|
+
definition: {
|
|
237
|
+
name: 'studio_create_artifact',
|
|
238
|
+
description: 'Create a new artifact record in a Decibel Studio project. Use this to register an artifact before uploading files.',
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: 'object',
|
|
241
|
+
properties: {
|
|
242
|
+
project_id: {
|
|
243
|
+
type: 'string',
|
|
244
|
+
description: 'Project UUID',
|
|
245
|
+
},
|
|
246
|
+
type: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
enum: ['image', 'video', '3d', 'audio', 'text'],
|
|
249
|
+
description: 'Artifact type',
|
|
250
|
+
},
|
|
251
|
+
prompt: {
|
|
252
|
+
type: 'string',
|
|
253
|
+
description: 'Generation prompt',
|
|
254
|
+
},
|
|
255
|
+
model: {
|
|
256
|
+
type: 'string',
|
|
257
|
+
description: 'Model used for generation',
|
|
258
|
+
},
|
|
259
|
+
provider: {
|
|
260
|
+
type: 'string',
|
|
261
|
+
description: 'Provider (e.g., openai, anthropic)',
|
|
262
|
+
},
|
|
263
|
+
parent_artifact_id: {
|
|
264
|
+
type: 'string',
|
|
265
|
+
description: 'Parent artifact UUID for lineage tracking',
|
|
266
|
+
},
|
|
267
|
+
workflow_id: {
|
|
268
|
+
type: 'string',
|
|
269
|
+
description: 'Workflow ID if part of a workflow run',
|
|
270
|
+
},
|
|
271
|
+
tags: {
|
|
272
|
+
type: 'array',
|
|
273
|
+
items: { type: 'string' },
|
|
274
|
+
description: 'Tags for organization',
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
required: ['project_id', 'type'],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
handler: async (args) => {
|
|
281
|
+
try {
|
|
282
|
+
requireFields(args, 'project_id', 'type');
|
|
283
|
+
if (!isSupabaseConfigured()) {
|
|
284
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
285
|
+
}
|
|
286
|
+
const client = getSupabaseClient();
|
|
287
|
+
log(`[Studio] Creating artifact in project ${args.project_id}`);
|
|
288
|
+
const insert = {
|
|
289
|
+
project_id: args.project_id,
|
|
290
|
+
type: args.type,
|
|
291
|
+
status: 'generating',
|
|
292
|
+
platform: 'claude-code',
|
|
293
|
+
app_version: '1.0.0',
|
|
294
|
+
prompt: args.prompt ?? null,
|
|
295
|
+
model: args.model ?? null,
|
|
296
|
+
provider: args.provider ?? null,
|
|
297
|
+
parent_artifact_id: args.parent_artifact_id ?? null,
|
|
298
|
+
workflow_id: args.workflow_id ?? null,
|
|
299
|
+
tags: args.tags ?? [],
|
|
300
|
+
};
|
|
301
|
+
const { data, error } = await client
|
|
302
|
+
.from('artifacts')
|
|
303
|
+
.insert(insert)
|
|
304
|
+
.select()
|
|
305
|
+
.single();
|
|
306
|
+
if (error) {
|
|
307
|
+
return toolError(formatSupabaseError(error).error);
|
|
308
|
+
}
|
|
309
|
+
return toolSuccess({
|
|
310
|
+
artifact: data,
|
|
311
|
+
message: 'Artifact created successfully',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
export const studioUpdateArtifactTool = {
|
|
320
|
+
definition: {
|
|
321
|
+
name: 'studio_update_artifact',
|
|
322
|
+
description: 'Update an artifact record (status, rating, pinned, etc).',
|
|
323
|
+
inputSchema: {
|
|
324
|
+
type: 'object',
|
|
325
|
+
properties: {
|
|
326
|
+
artifact_id: {
|
|
327
|
+
type: 'string',
|
|
328
|
+
description: 'Artifact UUID',
|
|
329
|
+
},
|
|
330
|
+
status: {
|
|
331
|
+
type: 'string',
|
|
332
|
+
enum: ['generating', 'partial', 'complete', 'failed'],
|
|
333
|
+
description: 'New status',
|
|
334
|
+
},
|
|
335
|
+
progress: {
|
|
336
|
+
type: 'number',
|
|
337
|
+
description: 'Progress 0.0-1.0',
|
|
338
|
+
},
|
|
339
|
+
package_url: {
|
|
340
|
+
type: 'string',
|
|
341
|
+
description: 'URL to .studio.zip in storage',
|
|
342
|
+
},
|
|
343
|
+
package_sha256: {
|
|
344
|
+
type: 'string',
|
|
345
|
+
description: 'SHA256 hash of the package',
|
|
346
|
+
},
|
|
347
|
+
is_pinned: {
|
|
348
|
+
type: 'boolean',
|
|
349
|
+
description: 'Pin/unpin the artifact',
|
|
350
|
+
},
|
|
351
|
+
rating: {
|
|
352
|
+
type: 'number',
|
|
353
|
+
description: 'Rating 1-5',
|
|
354
|
+
},
|
|
355
|
+
tags: {
|
|
356
|
+
type: 'array',
|
|
357
|
+
items: { type: 'string' },
|
|
358
|
+
description: 'Replace tags',
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
required: ['artifact_id'],
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
handler: async (args) => {
|
|
365
|
+
try {
|
|
366
|
+
requireFields(args, 'artifact_id');
|
|
367
|
+
if (!isSupabaseConfigured()) {
|
|
368
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
369
|
+
}
|
|
370
|
+
const client = getSupabaseClient();
|
|
371
|
+
log(`[Studio] Updating artifact ${args.artifact_id}`);
|
|
372
|
+
const update = {};
|
|
373
|
+
if (args.status !== undefined)
|
|
374
|
+
update.status = args.status;
|
|
375
|
+
if (args.progress !== undefined)
|
|
376
|
+
update.progress = args.progress;
|
|
377
|
+
if (args.package_url !== undefined)
|
|
378
|
+
update.package_url = args.package_url;
|
|
379
|
+
if (args.package_sha256 !== undefined)
|
|
380
|
+
update.package_sha256 = args.package_sha256;
|
|
381
|
+
if (args.is_pinned !== undefined)
|
|
382
|
+
update.is_pinned = args.is_pinned;
|
|
383
|
+
if (args.rating !== undefined)
|
|
384
|
+
update.rating = args.rating;
|
|
385
|
+
if (args.tags !== undefined)
|
|
386
|
+
update.tags = args.tags;
|
|
387
|
+
const { data, error } = await client
|
|
388
|
+
.from('artifacts')
|
|
389
|
+
.update(update)
|
|
390
|
+
.eq('id', args.artifact_id)
|
|
391
|
+
.select()
|
|
392
|
+
.single();
|
|
393
|
+
if (error) {
|
|
394
|
+
return toolError(formatSupabaseError(error).error);
|
|
395
|
+
}
|
|
396
|
+
return toolSuccess({
|
|
397
|
+
artifact: data,
|
|
398
|
+
message: 'Artifact updated successfully',
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
// ============================================================================
|
|
407
|
+
// Event Sync Tools
|
|
408
|
+
// ============================================================================
|
|
409
|
+
export const studioSyncEventsTool = {
|
|
410
|
+
definition: {
|
|
411
|
+
name: 'studio_sync_events',
|
|
412
|
+
description: 'Get events for a project since a given sequence number. Use for incremental sync.',
|
|
413
|
+
inputSchema: {
|
|
414
|
+
type: 'object',
|
|
415
|
+
properties: {
|
|
416
|
+
project_id: {
|
|
417
|
+
type: 'string',
|
|
418
|
+
description: 'Project UUID',
|
|
419
|
+
},
|
|
420
|
+
since_seq: {
|
|
421
|
+
type: 'number',
|
|
422
|
+
description: 'Return events after this sequence number (exclusive). Use 0 for initial sync.',
|
|
423
|
+
},
|
|
424
|
+
limit: {
|
|
425
|
+
type: 'number',
|
|
426
|
+
description: 'Maximum events to return (default: 100)',
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
required: ['project_id'],
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
handler: async (args) => {
|
|
433
|
+
try {
|
|
434
|
+
requireFields(args, 'project_id');
|
|
435
|
+
if (!isSupabaseConfigured()) {
|
|
436
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
437
|
+
}
|
|
438
|
+
const client = getSupabaseClient();
|
|
439
|
+
const sinceSeq = args.since_seq ?? 0;
|
|
440
|
+
const limit = args.limit ?? 100;
|
|
441
|
+
log(`[Studio] Syncing events for project ${args.project_id} since seq ${sinceSeq}`);
|
|
442
|
+
const { data, error } = await client
|
|
443
|
+
.from('events')
|
|
444
|
+
.select('*')
|
|
445
|
+
.eq('project_id', args.project_id)
|
|
446
|
+
.gt('seq', sinceSeq)
|
|
447
|
+
.order('seq', { ascending: true })
|
|
448
|
+
.limit(limit);
|
|
449
|
+
if (error) {
|
|
450
|
+
return toolError(formatSupabaseError(error).error);
|
|
451
|
+
}
|
|
452
|
+
const events = data ?? [];
|
|
453
|
+
const lastSeq = events.length > 0 ? events[events.length - 1].seq : sinceSeq;
|
|
454
|
+
return toolSuccess({
|
|
455
|
+
events,
|
|
456
|
+
count: events.length,
|
|
457
|
+
last_seq: lastSeq,
|
|
458
|
+
has_more: events.length === limit,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
catch (err) {
|
|
462
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
// ============================================================================
|
|
467
|
+
// Device Registration Tools
|
|
468
|
+
// ============================================================================
|
|
469
|
+
export const studioRegisterDeviceTool = {
|
|
470
|
+
definition: {
|
|
471
|
+
name: 'studio_register_device',
|
|
472
|
+
description: 'Register this device for Decibel Studio sync and job routing.',
|
|
473
|
+
inputSchema: {
|
|
474
|
+
type: 'object',
|
|
475
|
+
properties: {
|
|
476
|
+
name: {
|
|
477
|
+
type: 'string',
|
|
478
|
+
description: 'Device name (e.g., "Ben\'s MacBook Pro")',
|
|
479
|
+
},
|
|
480
|
+
capabilities: {
|
|
481
|
+
type: 'object',
|
|
482
|
+
description: 'Device capabilities (e.g., { "gpu": true, "local_generation": true })',
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
required: ['name'],
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
handler: async (args) => {
|
|
489
|
+
try {
|
|
490
|
+
requireFields(args, 'name');
|
|
491
|
+
if (!isSupabaseConfigured()) {
|
|
492
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
493
|
+
}
|
|
494
|
+
const client = getSupabaseClient();
|
|
495
|
+
// Get current user
|
|
496
|
+
const { data: { user }, error: authError } = await client.auth.getUser();
|
|
497
|
+
if (authError || !user) {
|
|
498
|
+
return toolError('Not authenticated. Please sign in first.');
|
|
499
|
+
}
|
|
500
|
+
log(`[Studio] Registering device: ${args.name}`);
|
|
501
|
+
const deviceData = {
|
|
502
|
+
user_id: user.id,
|
|
503
|
+
name: args.name,
|
|
504
|
+
platform: 'claude-code',
|
|
505
|
+
capabilities: args.capabilities ?? { claude_code: true },
|
|
506
|
+
last_seen_at: new Date().toISOString(),
|
|
507
|
+
};
|
|
508
|
+
// Upsert: insert or update existing device with same user_id + name
|
|
509
|
+
const { data, error } = await client
|
|
510
|
+
.from('devices')
|
|
511
|
+
.upsert(deviceData, {
|
|
512
|
+
onConflict: 'user_id,name',
|
|
513
|
+
ignoreDuplicates: false,
|
|
514
|
+
})
|
|
515
|
+
.select()
|
|
516
|
+
.single();
|
|
517
|
+
if (error) {
|
|
518
|
+
return toolError(formatSupabaseError(error).error);
|
|
519
|
+
}
|
|
520
|
+
return toolSuccess({
|
|
521
|
+
device: data,
|
|
522
|
+
message: 'Device registered successfully',
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
catch (err) {
|
|
526
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
};
|
|
530
|
+
export const studioHeartbeatTool = {
|
|
531
|
+
definition: {
|
|
532
|
+
name: 'studio_heartbeat',
|
|
533
|
+
description: 'Update device last_seen_at timestamp. Call periodically to keep device active.',
|
|
534
|
+
inputSchema: {
|
|
535
|
+
type: 'object',
|
|
536
|
+
properties: {
|
|
537
|
+
device_id: {
|
|
538
|
+
type: 'string',
|
|
539
|
+
description: 'Device UUID',
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
required: ['device_id'],
|
|
543
|
+
},
|
|
544
|
+
},
|
|
545
|
+
handler: async (args) => {
|
|
546
|
+
try {
|
|
547
|
+
requireFields(args, 'device_id');
|
|
548
|
+
if (!isSupabaseConfigured()) {
|
|
549
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
550
|
+
}
|
|
551
|
+
const client = getSupabaseClient();
|
|
552
|
+
const { error } = await client
|
|
553
|
+
.from('devices')
|
|
554
|
+
.update({ last_seen_at: new Date().toISOString() })
|
|
555
|
+
.eq('id', args.device_id);
|
|
556
|
+
if (error) {
|
|
557
|
+
return toolError(formatSupabaseError(error).error);
|
|
558
|
+
}
|
|
559
|
+
return toolSuccess({ message: 'Heartbeat recorded' });
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
};
|
|
566
|
+
// ============================================================================
|
|
567
|
+
// Job Tools
|
|
568
|
+
// ============================================================================
|
|
569
|
+
export const studioListJobsTool = {
|
|
570
|
+
definition: {
|
|
571
|
+
name: 'studio_list_jobs',
|
|
572
|
+
description: 'List pending jobs that can be claimed by this device.',
|
|
573
|
+
inputSchema: {
|
|
574
|
+
type: 'object',
|
|
575
|
+
properties: {
|
|
576
|
+
device_id: {
|
|
577
|
+
type: 'string',
|
|
578
|
+
description: 'This device\'s UUID (to find targeted jobs)',
|
|
579
|
+
},
|
|
580
|
+
status: {
|
|
581
|
+
type: 'string',
|
|
582
|
+
enum: ['pending', 'claimed', 'running', 'complete', 'failed', 'cancelled'],
|
|
583
|
+
description: 'Filter by status (default: pending)',
|
|
584
|
+
},
|
|
585
|
+
limit: {
|
|
586
|
+
type: 'number',
|
|
587
|
+
description: 'Maximum jobs to return (default: 20)',
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
handler: async (args) => {
|
|
593
|
+
try {
|
|
594
|
+
if (!isSupabaseConfigured()) {
|
|
595
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
596
|
+
}
|
|
597
|
+
const client = getSupabaseClient();
|
|
598
|
+
const status = args.status ?? 'pending';
|
|
599
|
+
const limit = args.limit ?? 20;
|
|
600
|
+
log(`[Studio] Listing jobs (status=${status})`);
|
|
601
|
+
let query = client
|
|
602
|
+
.from('jobs')
|
|
603
|
+
.select('*')
|
|
604
|
+
.eq('status', status)
|
|
605
|
+
.order('created_at', { ascending: true })
|
|
606
|
+
.limit(limit);
|
|
607
|
+
// If device_id provided, also include jobs targeted at this device
|
|
608
|
+
if (args.device_id) {
|
|
609
|
+
query = query.or(`target_device_id.is.null,target_device_id.eq.${args.device_id}`);
|
|
610
|
+
}
|
|
611
|
+
const { data, error } = await query;
|
|
612
|
+
if (error) {
|
|
613
|
+
return toolError(formatSupabaseError(error).error);
|
|
614
|
+
}
|
|
615
|
+
return toolSuccess({
|
|
616
|
+
jobs: data,
|
|
617
|
+
count: data?.length ?? 0,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
catch (err) {
|
|
621
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
622
|
+
}
|
|
623
|
+
},
|
|
624
|
+
};
|
|
625
|
+
export const studioClaimJobTool = {
|
|
626
|
+
definition: {
|
|
627
|
+
name: 'studio_claim_job',
|
|
628
|
+
description: 'Claim a pending job for this device to execute.',
|
|
629
|
+
inputSchema: {
|
|
630
|
+
type: 'object',
|
|
631
|
+
properties: {
|
|
632
|
+
job_id: {
|
|
633
|
+
type: 'string',
|
|
634
|
+
description: 'Job UUID to claim',
|
|
635
|
+
},
|
|
636
|
+
device_id: {
|
|
637
|
+
type: 'string',
|
|
638
|
+
description: 'This device\'s UUID',
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
required: ['job_id', 'device_id'],
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
handler: async (args) => {
|
|
645
|
+
try {
|
|
646
|
+
requireFields(args, 'job_id', 'device_id');
|
|
647
|
+
if (!isSupabaseConfigured()) {
|
|
648
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
649
|
+
}
|
|
650
|
+
const client = getSupabaseClient();
|
|
651
|
+
log(`[Studio] Claiming job ${args.job_id} for device ${args.device_id}`);
|
|
652
|
+
// Atomic claim: only update if still pending
|
|
653
|
+
const { data, error } = await client
|
|
654
|
+
.from('jobs')
|
|
655
|
+
.update({
|
|
656
|
+
status: 'claimed',
|
|
657
|
+
claimed_by_device_id: args.device_id,
|
|
658
|
+
claimed_at: new Date().toISOString(),
|
|
659
|
+
})
|
|
660
|
+
.eq('id', args.job_id)
|
|
661
|
+
.eq('status', 'pending')
|
|
662
|
+
.select()
|
|
663
|
+
.single();
|
|
664
|
+
if (error) {
|
|
665
|
+
return toolError(formatSupabaseError(error).error);
|
|
666
|
+
}
|
|
667
|
+
if (!data) {
|
|
668
|
+
return toolError('Job not found or already claimed');
|
|
669
|
+
}
|
|
670
|
+
return toolSuccess({
|
|
671
|
+
job: data,
|
|
672
|
+
message: 'Job claimed successfully',
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
catch (err) {
|
|
676
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
677
|
+
}
|
|
678
|
+
},
|
|
679
|
+
};
|
|
680
|
+
export const studioUpdateJobTool = {
|
|
681
|
+
definition: {
|
|
682
|
+
name: 'studio_update_job',
|
|
683
|
+
description: 'Update job progress or complete/fail the job.',
|
|
684
|
+
inputSchema: {
|
|
685
|
+
type: 'object',
|
|
686
|
+
properties: {
|
|
687
|
+
job_id: {
|
|
688
|
+
type: 'string',
|
|
689
|
+
description: 'Job UUID',
|
|
690
|
+
},
|
|
691
|
+
status: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
enum: ['running', 'complete', 'failed', 'cancelled'],
|
|
694
|
+
description: 'New status',
|
|
695
|
+
},
|
|
696
|
+
progress: {
|
|
697
|
+
type: 'number',
|
|
698
|
+
description: 'Progress 0.0-1.0',
|
|
699
|
+
},
|
|
700
|
+
error_message: {
|
|
701
|
+
type: 'string',
|
|
702
|
+
description: 'Error message if failed',
|
|
703
|
+
},
|
|
704
|
+
result_artifact_id: {
|
|
705
|
+
type: 'string',
|
|
706
|
+
description: 'Result artifact UUID if complete',
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
required: ['job_id'],
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
handler: async (args) => {
|
|
713
|
+
try {
|
|
714
|
+
requireFields(args, 'job_id');
|
|
715
|
+
if (!isSupabaseConfigured()) {
|
|
716
|
+
return toolError('Supabase not configured. Set SUPABASE_URL and SUPABASE_ANON_KEY.');
|
|
717
|
+
}
|
|
718
|
+
const client = getSupabaseClient();
|
|
719
|
+
log(`[Studio] Updating job ${args.job_id}`);
|
|
720
|
+
const update = {};
|
|
721
|
+
if (args.status !== undefined)
|
|
722
|
+
update.status = args.status;
|
|
723
|
+
if (args.progress !== undefined)
|
|
724
|
+
update.progress = args.progress;
|
|
725
|
+
if (args.error_message !== undefined)
|
|
726
|
+
update.error_message = args.error_message;
|
|
727
|
+
if (args.result_artifact_id !== undefined)
|
|
728
|
+
update.result_artifact_id = args.result_artifact_id;
|
|
729
|
+
if (args.status === 'complete' || args.status === 'failed') {
|
|
730
|
+
update.completed_at = new Date().toISOString();
|
|
731
|
+
}
|
|
732
|
+
const { data, error } = await client
|
|
733
|
+
.from('jobs')
|
|
734
|
+
.update(update)
|
|
735
|
+
.eq('id', args.job_id)
|
|
736
|
+
.select()
|
|
737
|
+
.single();
|
|
738
|
+
if (error) {
|
|
739
|
+
return toolError(formatSupabaseError(error).error);
|
|
740
|
+
}
|
|
741
|
+
return toolSuccess({
|
|
742
|
+
job: data,
|
|
743
|
+
message: 'Job updated successfully',
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
catch (err) {
|
|
747
|
+
return toolError(err instanceof Error ? err.message : String(err));
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
};
|
|
751
|
+
// ============================================================================
|
|
752
|
+
// Domain Export
|
|
753
|
+
// ============================================================================
|
|
754
|
+
export const studioCloudSpineTools = [
|
|
755
|
+
// Projects
|
|
756
|
+
studioListProjectsTool,
|
|
757
|
+
studioCreateProjectTool,
|
|
758
|
+
studioGetProjectTool,
|
|
759
|
+
// Artifacts
|
|
760
|
+
studioListArtifactsTool,
|
|
761
|
+
studioCreateArtifactTool,
|
|
762
|
+
studioUpdateArtifactTool,
|
|
763
|
+
// Events
|
|
764
|
+
studioSyncEventsTool,
|
|
765
|
+
// Devices
|
|
766
|
+
studioRegisterDeviceTool,
|
|
767
|
+
studioHeartbeatTool,
|
|
768
|
+
// Jobs
|
|
769
|
+
studioListJobsTool,
|
|
770
|
+
studioClaimJobTool,
|
|
771
|
+
studioUpdateJobTool,
|
|
772
|
+
];
|
|
773
|
+
//# sourceMappingURL=cloud-spine.js.map
|