dot-studio 0.0.1 → 0.0.2
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 +20 -200
- package/client/assets/ActFrame-BYOBkLYW.js +1 -0
- package/client/assets/ActFrame-C_WEt6bv.css +1 -0
- package/client/assets/ActInspectorPanel-C3VlS7tB.js +1 -0
- package/client/assets/ActInspectorPanel-CE6s6GYv.css +1 -0
- package/client/assets/AssistantChat-BOyW0K79.js +1 -0
- package/client/assets/AssistantChat-DoVmHvMJ.css +1 -0
- package/client/assets/CanvasTerminalFrame-BC-79q9U.css +1 -0
- package/client/assets/CanvasTerminalFrame-DxKbexK6.js +4 -0
- package/client/assets/CanvasTrackingFrame-DumxhNwg.js +1 -0
- package/client/assets/CanvasTrackingFrame-G4rRrfne.css +1 -0
- package/client/assets/CanvasWindowFrame-ziJeVfHG.js +1 -0
- package/client/assets/DanceBundleEditorFrame-CH8VDUMK.js +1 -0
- package/client/assets/DanceBundleEditorFrame-DaLqMflT.css +1 -0
- package/client/assets/MarkdownEditorFrame-DVecIZpZ.css +1 -0
- package/client/assets/MarkdownEditorFrame-Dwpgs2GX.js +2 -0
- package/client/assets/MarkdownRenderer-Cz8A4AgP.js +1 -0
- package/client/assets/PublishModal-DUlHz0fT.js +1 -0
- package/client/assets/TodoDock-DcVf7zQG.js +1 -0
- package/client/assets/WorkspaceToolbar-CXYi_sMD.js +2 -0
- package/client/assets/WorkspaceToolbar-CiQvVocC.css +1 -0
- package/client/assets/chat-message-visibility-YwJ-AQno.js +11 -0
- package/client/assets/dnd-vendor-CIAZE2P2.js +5 -0
- package/client/assets/flow-vendor-BZV40eAE.css +1 -0
- package/client/assets/flow-vendor-C868rU-6.js +23 -0
- package/client/assets/icon-vendor-I2JVIi1s.js +501 -0
- package/client/assets/index-BMY4hrBP.js +3 -0
- package/client/assets/index-C-vnj9y3.js +1 -0
- package/client/assets/index-C9HTqfZw.css +1 -0
- package/client/assets/index-CWrv6O3o.js +64 -0
- package/client/assets/index-DMS12-Q2.js +8 -0
- package/client/assets/index-Dn7t_Y7G.js +1 -0
- package/client/assets/index-p-wk7iGH.css +1 -0
- package/client/assets/markdown-vendor-BSTcku12.css +10 -0
- package/client/assets/markdown-vendor-DnTJ9hmR.js +35 -0
- package/client/assets/participant-labels-Cf3qP3GB.js +1 -0
- package/client/assets/queries-Dm1jEHfc.js +1 -0
- package/client/assets/query-vendor-_taqgrbn.js +1 -0
- package/client/assets/react-vendor-DzpMUNDT.js +49 -0
- package/client/assets/settings-utils-l7KCS3Ev.js +1 -0
- package/client/assets/terminal-vendor-6GBZ9nXN.css +32 -0
- package/client/assets/terminal-vendor-D0xRnmbI.js +112 -0
- package/client/index.html +13 -3
- package/dist/cli.js +25 -3
- package/dist/server/app.js +72 -0
- package/dist/server/index.js +2 -62
- package/dist/server/lib/act-session-policy.js +31 -0
- package/dist/server/lib/chat-session.js +101 -0
- package/dist/server/lib/config.js +18 -4
- package/dist/server/lib/dot-authoring.js +171 -102
- package/dist/server/lib/dot-loader.js +9 -8
- package/dist/server/lib/dot-login.js +8 -190
- package/dist/server/lib/dot-source.js +11 -0
- package/dist/server/lib/model-catalog.js +74 -15
- package/dist/server/lib/opencode-auth.js +4 -1
- package/dist/server/lib/opencode-errors.js +70 -38
- package/dist/server/lib/opencode-sidecar.js +5 -2
- package/dist/server/lib/project-config.js +8 -0
- package/dist/server/lib/runtime-tools.js +46 -8
- package/dist/server/lib/safe-mode.js +410 -0
- package/dist/server/lib/session-execution.js +81 -0
- package/dist/server/lib/sse.js +22 -0
- package/dist/server/routes/act-runtime-threads.js +156 -0
- package/dist/server/routes/act-runtime-tools.js +157 -0
- package/dist/server/routes/act-runtime.js +7 -0
- package/dist/server/routes/adapter.js +32 -0
- package/dist/server/routes/assets-collection.js +16 -0
- package/dist/server/routes/assets-detail.js +38 -0
- package/dist/server/routes/assets.js +4 -158
- package/dist/server/routes/chat-messages.js +104 -0
- package/dist/server/routes/chat-sessions.js +104 -0
- package/dist/server/routes/chat-stream.js +15 -0
- package/dist/server/routes/chat.js +6 -353
- package/dist/server/routes/compile.js +5 -91
- package/dist/server/routes/dot-assets.js +77 -0
- package/dist/server/routes/dot-core.js +62 -0
- package/dist/server/routes/dot-performer.js +80 -0
- package/dist/server/routes/dot.js +6 -267
- package/dist/server/routes/drafts-collection.js +40 -0
- package/dist/server/routes/drafts-dance-bundle.js +113 -0
- package/dist/server/routes/drafts-item.js +86 -0
- package/dist/server/routes/drafts.js +9 -0
- package/dist/server/routes/health.js +18 -33
- package/dist/server/routes/opencode-core.js +120 -0
- package/dist/server/routes/opencode-file.js +67 -0
- package/dist/server/routes/opencode-mcp.js +74 -0
- package/dist/server/routes/opencode-provider.js +41 -0
- package/dist/server/routes/opencode.js +8 -418
- package/dist/server/routes/route-errors.js +10 -0
- package/dist/server/routes/safe-actions.js +60 -0
- package/dist/server/routes/safe-summary.js +20 -0
- package/dist/server/routes/safe.js +7 -0
- package/dist/server/routes/workspaces.js +47 -0
- package/dist/server/services/act-runtime/act-context-builder.js +81 -0
- package/dist/server/services/act-runtime/act-runtime-service.js +313 -0
- package/dist/server/services/act-runtime/act-runtime-utils.js +10 -0
- package/dist/server/services/act-runtime/act-tool-projection.js +26 -0
- package/dist/server/services/act-runtime/act-tools.js +151 -0
- package/dist/server/services/act-runtime/board-persistence.js +38 -0
- package/dist/server/services/act-runtime/event-logger.js +73 -0
- package/dist/server/services/act-runtime/event-router.js +102 -0
- package/dist/server/services/act-runtime/mailbox.js +149 -0
- package/dist/server/services/act-runtime/safety-guard.js +162 -0
- package/dist/server/services/act-runtime/session-queue.js +114 -0
- package/dist/server/services/act-runtime/thread-manager.js +351 -0
- package/dist/server/services/act-runtime/wake-cascade.js +306 -0
- package/dist/server/services/act-runtime/wake-evaluator.js +43 -0
- package/dist/server/services/act-runtime/wake-performer-resolver.js +68 -0
- package/dist/server/services/act-runtime/wake-prompt-builder.js +77 -0
- package/dist/server/services/adapter-view-service.js +6 -0
- package/dist/server/services/asset-service.js +366 -0
- package/dist/server/services/chat-event-stream-service.js +157 -0
- package/dist/server/services/chat-service.js +207 -0
- package/dist/server/services/chat-session-service.js +203 -0
- package/dist/server/services/compile-service.js +4 -0
- package/dist/server/services/dance-bundle-service.js +222 -0
- package/dist/server/services/dot-add-service.js +59 -0
- package/dist/server/services/dot-service.js +178 -0
- package/dist/server/services/draft-service.js +367 -0
- package/dist/server/services/opencode-projection/dance-compiler.js +164 -0
- package/dist/server/services/opencode-projection/performer-compiler.js +195 -0
- package/dist/server/services/opencode-projection/preview-service.js +31 -0
- package/dist/server/services/opencode-projection/projection-manifest.js +98 -0
- package/dist/server/services/opencode-projection/stage-projection-service.js +188 -0
- package/dist/server/services/opencode-service.js +338 -0
- package/dist/server/services/safe-service.js +33 -0
- package/dist/server/services/studio-assistant/assistant-service.js +172 -0
- package/dist/server/services/studio-service.js +69 -0
- package/dist/server/services/workspace-service.js +224 -0
- package/dist/server/terminal.js +57 -11
- package/dist/shared/act-types.js +4 -0
- package/dist/shared/adapter-view.js +1 -0
- package/dist/shared/asset-contracts.js +1 -0
- package/dist/shared/assistant-actions.js +1 -0
- package/dist/shared/chat-contracts.js +1 -0
- package/dist/shared/dot-contracts.js +1 -0
- package/dist/shared/dot-types.js +4 -0
- package/dist/shared/draft-contracts.js +2 -0
- package/dist/shared/model-types.js +2 -0
- package/dist/shared/performer-mcp-portability.js +10 -0
- package/dist/shared/safe-mode.js +1 -0
- package/dist/shared/session-metadata.js +4 -3
- package/package.json +6 -4
- package/client/assets/index-C2eIILoa.css +0 -41
- package/client/assets/index-DUPZ_Lw5.js +0 -616
- package/dist/server/lib/act-runtime.js +0 -1282
- package/dist/server/lib/prompt.js +0 -222
- package/dist/server/routes/stages.js +0 -137
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { assetFilePath, ensureDotDir,
|
|
4
|
-
import { getPayloadTags, loadLocalAssetByUrn, parseUrn, publishSingleAsset, resolveDependencies, } from '
|
|
3
|
+
import { assetFilePath, danceAssetDir, ensureDotDir, getGlobalCwd } from './dot-source.js';
|
|
4
|
+
import { getPayloadTags, loadLocalAssetByUrn, parseDotAsset, parseUrn, publishSingleAsset, resolveDependencies, } from './dot-source.js';
|
|
5
5
|
const SLUG_RE = /^[a-z0-9][a-z0-9._-]{1,98}[a-z0-9]$/;
|
|
6
|
+
// Re-export auth helpers so dot-service.ts can import from one place
|
|
7
|
+
export { readAuthUser as readDotAuthUser, clearAuthUser as clearDotAuthUser } from './dot-source.js';
|
|
6
8
|
function isRecord(value) {
|
|
7
9
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
8
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Derives the URN stage from the working directory.
|
|
13
|
+
* Uses the sanitized basename of the cwd (e.g. /projects/my-app → my-app).
|
|
14
|
+
*/
|
|
15
|
+
function stageFromCwd(cwd) {
|
|
16
|
+
const base = path.basename(cwd);
|
|
17
|
+
// Sanitize to lowercase slug format
|
|
18
|
+
return base
|
|
19
|
+
.toLowerCase()
|
|
20
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
21
|
+
.replace(/^-+|-+$/g, '')
|
|
22
|
+
|| 'default';
|
|
23
|
+
}
|
|
9
24
|
function sanitizeSlug(value) {
|
|
10
25
|
const slug = value.trim();
|
|
11
26
|
if (!SLUG_RE.test(slug)) {
|
|
@@ -29,11 +44,8 @@ function sanitizeAuthor(value) {
|
|
|
29
44
|
}
|
|
30
45
|
return author;
|
|
31
46
|
}
|
|
32
|
-
function
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
function normalizeDescription(name, value) {
|
|
36
|
-
return typeof value === 'string' && value.trim() ? value.trim() : name;
|
|
47
|
+
function normalizeDescription(value) {
|
|
48
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
37
49
|
}
|
|
38
50
|
function ensureUrn(value, kind) {
|
|
39
51
|
if (typeof value !== 'string') {
|
|
@@ -41,80 +53,79 @@ function ensureUrn(value, kind) {
|
|
|
41
53
|
}
|
|
42
54
|
const urn = value.trim();
|
|
43
55
|
const parts = urn.split('/');
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
// Accept both 3-segment (kind/@owner/name) and 4-segment (kind/@owner/stage/name)
|
|
57
|
+
const validLength = parts.length === 3 || parts.length === 4;
|
|
58
|
+
if (!validLength || parts[0] !== kind || !parts[1].startsWith('@') || !parts[parts.length - 1]) {
|
|
59
|
+
throw new Error(`Invalid URN '${urn}'. Expected ${kind}/@<owner>/<name> or ${kind}/@<owner>/<stage>/<name>.`);
|
|
46
60
|
}
|
|
47
61
|
return urn;
|
|
48
62
|
}
|
|
63
|
+
function finalizeAsset(asset) {
|
|
64
|
+
return parseDotAsset(asset);
|
|
65
|
+
}
|
|
49
66
|
function normalizeTalPayload(author, slug, payload) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
name,
|
|
56
|
-
description: normalizeDescription(name, payload.description),
|
|
67
|
+
return finalizeAsset({
|
|
68
|
+
$schema: 'https://schemas.danceoftal.com/assets/tal.v1.json',
|
|
69
|
+
kind: 'tal',
|
|
70
|
+
urn: `tal/@${author}/${slug}`,
|
|
71
|
+
...(normalizeDescription(payload.description) ? { description: normalizeDescription(payload.description) } : {}),
|
|
57
72
|
tags: sanitizeTags(payload.tags),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
73
|
+
payload: {
|
|
74
|
+
content: typeof payload.content === 'string' ? payload.content : '',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
62
77
|
}
|
|
63
|
-
function normalizeDancePayload(author, slug, payload) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
name,
|
|
70
|
-
description: normalizeDescription(name, payload.description),
|
|
78
|
+
function normalizeDancePayload(author, stage, slug, payload) {
|
|
79
|
+
return finalizeAsset({
|
|
80
|
+
$schema: 'https://schemas.danceoftal.com/assets/dance.v1.json',
|
|
81
|
+
kind: 'dance',
|
|
82
|
+
urn: `dance/@${author}/${stage}/${slug}`,
|
|
83
|
+
...(normalizeDescription(payload.description) ? { description: normalizeDescription(payload.description) } : {}),
|
|
71
84
|
tags: sanitizeTags(payload.tags),
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
};
|
|
85
|
+
payload: {
|
|
86
|
+
content: typeof payload.content === 'string' ? payload.content : '',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
76
89
|
}
|
|
77
90
|
function normalizePerformerPayload(author, slug, payload) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? ensureUrn(danceValue, 'dance')
|
|
82
|
-
: Array.isArray(danceValue)
|
|
83
|
-
? danceValue.map((value) => ensureUrn(value, 'dance'))
|
|
84
|
-
: undefined;
|
|
91
|
+
const dances = Array.isArray(payload.dances)
|
|
92
|
+
? payload.dances.map((value) => ensureUrn(value, 'dance'))
|
|
93
|
+
: undefined;
|
|
85
94
|
const tal = payload.tal !== undefined && payload.tal !== null ? ensureUrn(payload.tal, 'tal') : undefined;
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
95
|
+
let modelValue = undefined;
|
|
96
|
+
if (isRecord(payload.model)
|
|
97
|
+
&& typeof payload.model.provider === 'string'
|
|
98
|
+
&& typeof payload.model.modelId === 'string') {
|
|
99
|
+
modelValue = payload.model;
|
|
89
100
|
}
|
|
90
|
-
return {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
description: normalizeDescription(
|
|
101
|
+
return finalizeAsset({
|
|
102
|
+
$schema: 'https://schemas.danceoftal.com/assets/performer.v1.json',
|
|
103
|
+
kind: 'performer',
|
|
104
|
+
urn: `performer/@${author}/${slug}`,
|
|
105
|
+
...(normalizeDescription(payload.description) ? { description: normalizeDescription(payload.description) } : {}),
|
|
95
106
|
tags: sanitizeTags(payload.tags),
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
? {
|
|
99
|
-
: {}),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
};
|
|
107
|
+
payload: {
|
|
108
|
+
...(tal ? { tal } : {}),
|
|
109
|
+
...(dances ? { dances } : {}),
|
|
110
|
+
...(modelValue !== undefined ? { model: modelValue } : {}),
|
|
111
|
+
...(typeof payload.modelVariant === 'string' && payload.modelVariant.trim() ? { modelVariant: payload.modelVariant.trim() } : {}),
|
|
112
|
+
...(isRecord(payload.mcp_config) ? { mcp_config: payload.mcp_config } : {}),
|
|
113
|
+
},
|
|
114
|
+
});
|
|
104
115
|
}
|
|
105
116
|
function normalizeActPayload(author, slug, payload) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
slug
|
|
110
|
-
|
|
111
|
-
description: normalizeDescription(name, payload.description),
|
|
117
|
+
return finalizeAsset({
|
|
118
|
+
$schema: 'https://schemas.danceoftal.com/assets/act.v1.json',
|
|
119
|
+
kind: 'act',
|
|
120
|
+
urn: `act/@${author}/${slug}`,
|
|
121
|
+
...(normalizeDescription(payload.description) ? { description: normalizeDescription(payload.description) } : {}),
|
|
112
122
|
tags: sanitizeTags(payload.tags),
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
123
|
+
payload: {
|
|
124
|
+
...(Array.isArray(payload.actRules) ? { actRules: payload.actRules } : {}),
|
|
125
|
+
participants: Array.isArray(payload.participants) ? payload.participants : [],
|
|
126
|
+
relations: Array.isArray(payload.relations) ? payload.relations : [],
|
|
127
|
+
},
|
|
128
|
+
});
|
|
118
129
|
}
|
|
119
130
|
export function normalizeStudioAssetPayload(kind, authorInput, slugInput, payloadInput) {
|
|
120
131
|
if (!isRecord(payloadInput)) {
|
|
@@ -126,43 +137,52 @@ export function normalizeStudioAssetPayload(kind, authorInput, slugInput, payloa
|
|
|
126
137
|
case 'tal':
|
|
127
138
|
return normalizeTalPayload(author, slug, payloadInput);
|
|
128
139
|
case 'dance':
|
|
129
|
-
|
|
140
|
+
// Dance URN requires a stage — caller must pass stage in slug as 'stage/name'
|
|
141
|
+
// or use saveLocalStudioAsset which derives stage from cwd
|
|
142
|
+
return normalizeDancePayload(author, 'default', slug, payloadInput);
|
|
130
143
|
case 'performer':
|
|
131
144
|
return normalizePerformerPayload(author, slug, payloadInput);
|
|
132
145
|
case 'act':
|
|
133
146
|
return normalizeActPayload(author, slug, payloadInput);
|
|
134
147
|
}
|
|
135
148
|
}
|
|
136
|
-
export async function
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
149
|
+
export async function saveLocalStudioAsset(options) {
|
|
150
|
+
const author = sanitizeAuthor(options.author);
|
|
151
|
+
const slug = sanitizeSlug(options.slug);
|
|
152
|
+
const stage = stageFromCwd(options.cwd);
|
|
153
|
+
// All Studio assets use 4-segment URN: kind/@author/stage/name
|
|
154
|
+
const urn = `${options.kind}/@${author}/${stage}/${slug}`;
|
|
155
|
+
await ensureDotDir(options.cwd);
|
|
156
|
+
if (options.kind === 'dance') {
|
|
157
|
+
// Dance is a bundle directory — write SKILL.md with proper frontmatter
|
|
158
|
+
const bundleDir = danceAssetDir(options.cwd, urn);
|
|
159
|
+
const skillMdPath = path.join(bundleDir, 'SKILL.md');
|
|
160
|
+
await fs.mkdir(bundleDir, { recursive: true });
|
|
161
|
+
if (!isRecord(options.payload)) {
|
|
162
|
+
throw new Error('Dance payload must be a JSON object.');
|
|
142
163
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
throw error;
|
|
164
|
+
const content = typeof options.payload.content === 'string' ? options.payload.content : '';
|
|
165
|
+
const description = typeof options.payload.description === 'string' ? options.payload.description : '';
|
|
166
|
+
const frontmatter = [
|
|
167
|
+
'---',
|
|
168
|
+
`name: ${JSON.stringify(slug)}`,
|
|
169
|
+
...(description ? [`description: ${JSON.stringify(description)}`] : []),
|
|
170
|
+
'---',
|
|
171
|
+
].join('\n');
|
|
172
|
+
const skillMdContent = `${frontmatter}\n\n${content}`;
|
|
173
|
+
let existed = true;
|
|
174
|
+
try {
|
|
175
|
+
await fs.access(skillMdPath);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
existed = false;
|
|
159
179
|
}
|
|
180
|
+
await fs.writeFile(skillMdPath, skillMdContent, 'utf-8');
|
|
181
|
+
return { urn, path: skillMdPath, existed, payload: options.payload };
|
|
160
182
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const urn = `${options.kind}/@${sanitizeAuthor(options.author)}/${sanitizeSlug(options.slug)}`;
|
|
165
|
-
await ensureDotDir(options.cwd);
|
|
183
|
+
// Tal / Performer / Act — JSON file with 4-segment URN embedded
|
|
184
|
+
const normalized = normalizeStudioAssetPayload(options.kind, author, slug, options.payload);
|
|
185
|
+
normalized.urn = urn;
|
|
166
186
|
const filePath = assetFilePath(options.cwd, urn);
|
|
167
187
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
168
188
|
let existed = true;
|
|
@@ -173,12 +193,7 @@ export async function saveLocalStudioAsset(options) {
|
|
|
173
193
|
existed = false;
|
|
174
194
|
}
|
|
175
195
|
await fs.writeFile(filePath, JSON.stringify(normalized, null, 2), 'utf-8');
|
|
176
|
-
return {
|
|
177
|
-
urn,
|
|
178
|
-
path: filePath,
|
|
179
|
-
existed,
|
|
180
|
-
payload: normalized,
|
|
181
|
-
};
|
|
196
|
+
return { urn, path: filePath, existed, payload: normalized };
|
|
182
197
|
}
|
|
183
198
|
export async function publishStudioAsset(options) {
|
|
184
199
|
const slug = sanitizeSlug(options.slug);
|
|
@@ -223,7 +238,7 @@ export async function publishStudioAsset(options) {
|
|
|
223
238
|
if (!parsed) {
|
|
224
239
|
continue;
|
|
225
240
|
}
|
|
226
|
-
const published = await publishSingleAsset(parsed.kind, parsed.name, dependency.payload, sanitizeTags(getPayloadTags(dependency.payload)), options.auth.token);
|
|
241
|
+
const published = await publishSingleAsset(parsed.kind, parsed.stage, parsed.name, dependency.payload, sanitizeTags(getPayloadTags(dependency.payload)), options.auth.token);
|
|
227
242
|
if (published) {
|
|
228
243
|
dependenciesPublished.push(dependency.urn);
|
|
229
244
|
}
|
|
@@ -232,8 +247,20 @@ export async function publishStudioAsset(options) {
|
|
|
232
247
|
}
|
|
233
248
|
}
|
|
234
249
|
}
|
|
250
|
+
// Dance assets are not publishable through the Studio registry pipeline
|
|
251
|
+
if (options.kind === 'dance') {
|
|
252
|
+
throw new Error('Dance assets cannot be published via the Studio registry pipeline. Use `dot add` to register from GitHub.');
|
|
253
|
+
}
|
|
254
|
+
const publishKind = options.kind;
|
|
255
|
+
// publishStudioAsset receives `options.slug` which is name only; stage is embedded in URN.
|
|
256
|
+
// For the studio, all assets use the stage from `parseUrn` of the built URN.
|
|
257
|
+
// The registry API needs (kind, stage, name, ...) — stage is the slug middle segment.
|
|
258
|
+
// Studio draft slugs are simple (no stage prefix), so we use the username as the stage owner.
|
|
259
|
+
// For now stage == slug to maintain backward compat (studio always publishes to the user's root stage).
|
|
235
260
|
const tags = options.tags && options.tags.length > 0 ? sanitizeTags(options.tags) : sanitizeTags(getPayloadTags(localPayload));
|
|
236
|
-
const
|
|
261
|
+
const publishUrn = parseUrn(`${publishKind}/@${username}/${slug}`);
|
|
262
|
+
const publishStage = publishUrn?.stage ?? slug;
|
|
263
|
+
const mainPublished = await publishSingleAsset(publishKind, publishStage, slug, localPayload, tags, options.auth.token);
|
|
237
264
|
const urn = `${options.kind}/@${username}/${slug}`;
|
|
238
265
|
return {
|
|
239
266
|
urn,
|
|
@@ -243,3 +270,45 @@ export async function publishStudioAsset(options) {
|
|
|
243
270
|
dependenciesExisting,
|
|
244
271
|
};
|
|
245
272
|
}
|
|
273
|
+
export async function uninstallStudioAsset(cwd, urn) {
|
|
274
|
+
const [kind] = urn.split('/');
|
|
275
|
+
if (kind === 'dance') {
|
|
276
|
+
// Dance is a directory bundle — remove the whole directory
|
|
277
|
+
const tryRemoveDir = async (dir) => {
|
|
278
|
+
try {
|
|
279
|
+
await fs.access(dir);
|
|
280
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
if (await tryRemoveDir(danceAssetDir(cwd, urn))) {
|
|
288
|
+
return { urn, scope: 'stage' };
|
|
289
|
+
}
|
|
290
|
+
if (await tryRemoveDir(danceAssetDir(getGlobalCwd(), urn))) {
|
|
291
|
+
return { urn, scope: 'global' };
|
|
292
|
+
}
|
|
293
|
+
throw new Error(`Dance asset not found: ${urn}`);
|
|
294
|
+
}
|
|
295
|
+
// Tal / Performer / Act — single JSON file
|
|
296
|
+
const stagePath = assetFilePath(cwd, urn);
|
|
297
|
+
try {
|
|
298
|
+
await fs.access(stagePath);
|
|
299
|
+
await fs.unlink(stagePath);
|
|
300
|
+
return { urn, scope: 'stage' };
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
// Not found in stage, try global
|
|
304
|
+
}
|
|
305
|
+
const globalPath = assetFilePath(getGlobalCwd(), urn);
|
|
306
|
+
try {
|
|
307
|
+
await fs.access(globalPath);
|
|
308
|
+
await fs.unlink(globalPath);
|
|
309
|
+
return { urn, scope: 'global' };
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
throw new Error(`Asset not found: ${urn}`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
@@ -27,17 +27,18 @@ export async function ensureDotLoaderServer(cwd) {
|
|
|
27
27
|
const statusData = unwrapOpencodeResult(await oc.mcp.status(params)) || {};
|
|
28
28
|
const existing = statusData[serverName];
|
|
29
29
|
if (!existing) {
|
|
30
|
+
const config = {
|
|
31
|
+
type: 'local',
|
|
32
|
+
command: [resolveDotCommand()],
|
|
33
|
+
enabled: true,
|
|
34
|
+
environment: {
|
|
35
|
+
DANCE_OF_TAL_PROJECT_DIR: path.resolve(cwd),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
30
38
|
unwrapOpencodeResult(await oc.mcp.add({
|
|
31
39
|
...params,
|
|
32
40
|
name: serverName,
|
|
33
|
-
config
|
|
34
|
-
type: 'local',
|
|
35
|
-
command: [resolveDotCommand()],
|
|
36
|
-
enabled: true,
|
|
37
|
-
environment: {
|
|
38
|
-
DANCE_OF_TAL_PROJECT_DIR: path.resolve(cwd),
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
+
config,
|
|
41
42
|
}));
|
|
42
43
|
}
|
|
43
44
|
const refreshedStatusData = unwrapOpencodeResult(await oc.mcp.status(params)) || {};
|
|
@@ -1,190 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const SUPABASE_ANON_KEY = process.env.DOT_SUPABASE_ANON_KEY || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InFiaWxkY3JmamVuY29xa25neWZ3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIyNjE5MzYsImV4cCI6MjA4NzgzNzkzNn0.9aI9FU-j20w3UIG7BuVtmpAPh3qClz7xTNXjcq7ofNQ';
|
|
10
|
-
const DOT_LOGIN_REDIRECT_URI = 'http://localhost:4242/callback';
|
|
11
|
-
const LOGIN_SERVER_TIMEOUT_MS = 180_000;
|
|
12
|
-
let loginState = null;
|
|
13
|
-
function getAuthFilePath() {
|
|
14
|
-
return path.join(getGlobalDotDir(), 'auth.json');
|
|
15
|
-
}
|
|
16
|
-
async function saveAuthToken(token, username) {
|
|
17
|
-
const authFile = getAuthFilePath();
|
|
18
|
-
await fs.mkdir(path.dirname(authFile), { recursive: true });
|
|
19
|
-
await fs.writeFile(authFile, JSON.stringify({ token, username }, null, 2), 'utf-8');
|
|
20
|
-
}
|
|
21
|
-
function generateCodeVerifier() {
|
|
22
|
-
return crypto.randomBytes(32).toString('base64url');
|
|
23
|
-
}
|
|
24
|
-
function generateCodeChallenge(verifier) {
|
|
25
|
-
return crypto.createHash('sha256').update(verifier).digest('base64url');
|
|
26
|
-
}
|
|
27
|
-
function clearLoginState() {
|
|
28
|
-
if (!loginState) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
clearTimeout(loginState.timeout);
|
|
32
|
-
loginState.server.close();
|
|
33
|
-
loginState = null;
|
|
34
|
-
}
|
|
35
|
-
async function releaseStaleLoginPort() {
|
|
36
|
-
const controller = new AbortController();
|
|
37
|
-
const timer = setTimeout(() => controller.abort(), 1_500);
|
|
38
|
-
try {
|
|
39
|
-
// Legacy dot login closes itself when /callback is hit without a code.
|
|
40
|
-
await fetch(DOT_LOGIN_REDIRECT_URI, {
|
|
41
|
-
method: 'GET',
|
|
42
|
-
signal: controller.signal,
|
|
43
|
-
}).catch(() => { });
|
|
44
|
-
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
45
|
-
}
|
|
46
|
-
finally {
|
|
47
|
-
clearTimeout(timer);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async function listenOnLoginPort(server) {
|
|
51
|
-
const tryListen = async () => new Promise((resolve, reject) => {
|
|
52
|
-
const onError = (error) => {
|
|
53
|
-
server.off('listening', onListening);
|
|
54
|
-
reject(error);
|
|
55
|
-
};
|
|
56
|
-
const onListening = () => {
|
|
57
|
-
server.off('error', onError);
|
|
58
|
-
resolve();
|
|
59
|
-
};
|
|
60
|
-
server.once('error', onError);
|
|
61
|
-
server.once('listening', onListening);
|
|
62
|
-
server.listen(4242);
|
|
63
|
-
});
|
|
64
|
-
try {
|
|
65
|
-
await tryListen();
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
if (error?.code !== 'EADDRINUSE') {
|
|
69
|
-
throw error;
|
|
70
|
-
}
|
|
71
|
-
await releaseStaleLoginPort();
|
|
72
|
-
try {
|
|
73
|
-
await tryListen();
|
|
74
|
-
}
|
|
75
|
-
catch (retryError) {
|
|
76
|
-
if (retryError?.code === 'EADDRINUSE') {
|
|
77
|
-
throw new Error('Port 4242 is already in use by another process. Finish or close the other DOT login flow, then try again.');
|
|
78
|
-
}
|
|
79
|
-
throw retryError;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export async function startDotLogin() {
|
|
84
|
-
const auth = await readDotAuthUser();
|
|
85
|
-
if (auth) {
|
|
86
|
-
return {
|
|
87
|
-
started: false,
|
|
88
|
-
alreadyAuthenticated: true,
|
|
89
|
-
username: auth.username,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
if (loginState) {
|
|
93
|
-
return {
|
|
94
|
-
started: false,
|
|
95
|
-
alreadyRunning: true,
|
|
96
|
-
authUrl: loginState.authUrl,
|
|
97
|
-
browserOpened: false,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
const codeVerifier = generateCodeVerifier();
|
|
101
|
-
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
102
|
-
const authUrl = `${SUPABASE_URL}/auth/v1/authorize?provider=github&redirect_to=${encodeURIComponent(DOT_LOGIN_REDIRECT_URI)}&code_challenge=${codeChallenge}&code_challenge_method=s256`;
|
|
103
|
-
const server = http.createServer(async (req, res) => {
|
|
104
|
-
try {
|
|
105
|
-
const url = new URL(req.url || '/', DOT_LOGIN_REDIRECT_URI);
|
|
106
|
-
if (url.pathname !== '/callback') {
|
|
107
|
-
res.writeHead(404).end('Not Found');
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const code = url.searchParams.get('code');
|
|
111
|
-
if (!code) {
|
|
112
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
113
|
-
res.end("<h2 style='color: red; text-align: center; font-family: sans-serif; margin-top: 50px;'>Authentication failed: No code received. You can close this window.</h2>");
|
|
114
|
-
clearLoginState();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
118
|
-
res.write("<h2 style='font-family: sans-serif; text-align: center; margin-top: 50px;'>Completing authentication... Please wait.</h2>");
|
|
119
|
-
try {
|
|
120
|
-
const tokenRes = await fetch(`${SUPABASE_URL}/auth/v1/token?grant_type=pkce`, {
|
|
121
|
-
method: 'POST',
|
|
122
|
-
headers: {
|
|
123
|
-
'Content-Type': 'application/json',
|
|
124
|
-
apikey: SUPABASE_ANON_KEY,
|
|
125
|
-
},
|
|
126
|
-
body: JSON.stringify({
|
|
127
|
-
auth_code: code,
|
|
128
|
-
code_verifier: codeVerifier,
|
|
129
|
-
}),
|
|
130
|
-
});
|
|
131
|
-
const data = await tokenRes.json();
|
|
132
|
-
if (!tokenRes.ok || !data.access_token) {
|
|
133
|
-
throw new Error(data.error_description || data.msg || 'Failed to exchange token');
|
|
134
|
-
}
|
|
135
|
-
const username = data.user?.user_metadata?.preferred_username || data.user?.user_metadata?.user_name;
|
|
136
|
-
if (!username) {
|
|
137
|
-
throw new Error('Could not determine GitHub username from token.');
|
|
138
|
-
}
|
|
139
|
-
await saveAuthToken(data.access_token, username);
|
|
140
|
-
res.end(`
|
|
141
|
-
<script>
|
|
142
|
-
document.body.innerHTML = "<h2 style='color: green; font-family: sans-serif; text-align: center; margin-top: 50px;'>Authentication Successful! You can safely close this window.</h2>";
|
|
143
|
-
setTimeout(() => window.close(), 3000);
|
|
144
|
-
</script>
|
|
145
|
-
`);
|
|
146
|
-
}
|
|
147
|
-
catch (error) {
|
|
148
|
-
res.end(`
|
|
149
|
-
<script>
|
|
150
|
-
document.body.innerHTML = "<h2 style='color: red; font-family: sans-serif; text-align: center; margin-top: 50px;'>Authentication Failed. ${String(error?.message || 'Unknown error')}</h2>";
|
|
151
|
-
</script>
|
|
152
|
-
`);
|
|
153
|
-
}
|
|
154
|
-
finally {
|
|
155
|
-
clearLoginState();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
try {
|
|
160
|
-
res.writeHead(500).end('Server Error');
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
// ignore
|
|
164
|
-
}
|
|
165
|
-
clearLoginState();
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
await listenOnLoginPort(server);
|
|
169
|
-
loginState = {
|
|
170
|
-
server,
|
|
171
|
-
authUrl,
|
|
172
|
-
timeout: setTimeout(() => {
|
|
173
|
-
clearLoginState();
|
|
174
|
-
}, LOGIN_SERVER_TIMEOUT_MS),
|
|
175
|
-
};
|
|
176
|
-
let browserOpened = true;
|
|
177
|
-
try {
|
|
178
|
-
await open(authUrl);
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
browserOpened = false;
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
started: true,
|
|
185
|
-
alreadyRunning: false,
|
|
186
|
-
alreadyAuthenticated: false,
|
|
187
|
-
authUrl,
|
|
188
|
-
browserOpened,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Studio server — thin re-export of dot lib login.
|
|
3
|
+
* Auth logic lives in dance-of-tal/lib/auth; this file is kept for
|
|
4
|
+
* backward-compatible imports within the studio server.
|
|
5
|
+
*/
|
|
6
|
+
export { startLogin, readAuthUser, saveAuthToken, clearAuthUser } from './dot-source.js';
|
|
7
|
+
// Backward-compatible alias used by dot-service.ts
|
|
8
|
+
export { startLogin as startDotLogin } from './dot-source.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// dot Package Re-exports — Server Only
|
|
2
|
+
// Functions from dot that require Node.js runtime (file I/O, HTTP, etc.)
|
|
3
|
+
// For dot TYPES, import from 'shared/dot-types.ts' instead.
|
|
4
|
+
export { assetFilePath, danceAssetDir, ensureDotDir, getAssetPayload, getDotDir, getGlobalCwd, getGlobalDotDir, initRegistry, readAsset, } from 'dance-of-tal/lib/registry';
|
|
5
|
+
export { getRegistryPackage, reportInstall, searchRegistry, } from 'dance-of-tal/lib/registry-api';
|
|
6
|
+
export { installActWithDependencies, installPerformerWithDeps, } from 'dance-of-tal/lib/dependency-resolver';
|
|
7
|
+
export { installAsset, } from 'dance-of-tal/lib/installer';
|
|
8
|
+
export { readAuthUser, saveAuthToken, clearAuthUser, startLogin, } from 'dance-of-tal/lib/auth';
|
|
9
|
+
export { getPayloadTags, loadLocalAssetByUrn, parseUrn, publishSingleAsset, resolveDependencies, } from 'dance-of-tal/lib/publishing';
|
|
10
|
+
export { parseDotAsset, parsePerformerAsset, parseActAsset, slugFromUrn, } from 'dance-of-tal/contracts';
|
|
11
|
+
export { parseSource, getOwnerRepo, shallowClone, discoverSkills, copySkillDir, upsertSkillLockEntry, readPluginManifest, } from 'dance-of-tal/lib/add';
|