opencube 0.2.0 → 0.3.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/package.json +1 -1
- package/src/main.js +173 -14
- package/src/plugin-server.cjs +69 -0
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -26,6 +26,8 @@ let interactionEvents = []
|
|
|
26
26
|
let petSignals = []
|
|
27
27
|
let sessionMap = new Map()
|
|
28
28
|
let activeToolsBySession = new Map()
|
|
29
|
+
let pendingPermissionsByRequest = new Map()
|
|
30
|
+
let pendingQuestionsByRequest = new Map()
|
|
29
31
|
let cleanupTimer = null
|
|
30
32
|
let dragState = null
|
|
31
33
|
|
|
@@ -131,6 +133,8 @@ function recordEvent(event) {
|
|
|
131
133
|
}
|
|
132
134
|
applySessionEvent(item)
|
|
133
135
|
applyToolEvent(item)
|
|
136
|
+
applyPermissionEvent(item)
|
|
137
|
+
applyQuestionEvent(item)
|
|
134
138
|
if (item.type === "hello" || item.type === "fancy_hello") {
|
|
135
139
|
petSignals.push({
|
|
136
140
|
id: item.id,
|
|
@@ -148,6 +152,63 @@ function recordEvent(event) {
|
|
|
148
152
|
return item
|
|
149
153
|
}
|
|
150
154
|
|
|
155
|
+
function applyPermissionEvent(event) {
|
|
156
|
+
if (!event || typeof event.sessionID !== "string") return
|
|
157
|
+
|
|
158
|
+
if (event.type === "permission.ask") {
|
|
159
|
+
const requestID = typeof event.requestID === "string" ? event.requestID : event.id
|
|
160
|
+
if (!requestID) return
|
|
161
|
+
pendingPermissionsByRequest.set(requestID, {
|
|
162
|
+
requestID,
|
|
163
|
+
sessionID: event.sessionID,
|
|
164
|
+
permission: event.permission,
|
|
165
|
+
patterns: event.patterns,
|
|
166
|
+
metadata: event.metadata,
|
|
167
|
+
always: event.always,
|
|
168
|
+
tool: event.tool,
|
|
169
|
+
askedAt: event.receivedAt || Date.now(),
|
|
170
|
+
})
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (event.type === "permission.reply") {
|
|
175
|
+
if (typeof event.requestID === "string") pendingPermissionsByRequest.delete(event.requestID)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function clearPendingPermissionsForSession(sessionID) {
|
|
180
|
+
for (const [requestID, permission] of pendingPermissionsByRequest) {
|
|
181
|
+
if (permission?.sessionID === sessionID) pendingPermissionsByRequest.delete(requestID)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function applyQuestionEvent(event) {
|
|
186
|
+
if (!event || typeof event.sessionID !== "string") return
|
|
187
|
+
|
|
188
|
+
if (event.type === "question.ask") {
|
|
189
|
+
const requestID = typeof event.requestID === "string" ? event.requestID : event.id
|
|
190
|
+
if (!requestID) return
|
|
191
|
+
pendingQuestionsByRequest.set(requestID, {
|
|
192
|
+
requestID,
|
|
193
|
+
sessionID: event.sessionID,
|
|
194
|
+
questions: event.questions,
|
|
195
|
+
tool: event.tool,
|
|
196
|
+
askedAt: event.receivedAt || Date.now(),
|
|
197
|
+
})
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (event.type === "question.reply" || event.type === "question.reject") {
|
|
202
|
+
if (typeof event.requestID === "string") pendingQuestionsByRequest.delete(event.requestID)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function clearPendingQuestionsForSession(sessionID) {
|
|
207
|
+
for (const [requestID, question] of pendingQuestionsByRequest) {
|
|
208
|
+
if (question?.sessionID === sessionID) pendingQuestionsByRequest.delete(requestID)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
151
212
|
function applyToolEvent(event) {
|
|
152
213
|
if (!event || typeof event.sessionID !== "string" || typeof event.callID !== "string") return
|
|
153
214
|
if (event.type !== "tool.start" && event.type !== "tool.finish") return
|
|
@@ -204,6 +265,8 @@ function applySessionEvent(event) {
|
|
|
204
265
|
|
|
205
266
|
if (event.type === "session.idle") {
|
|
206
267
|
activeToolsBySession.delete(event.sessionID)
|
|
268
|
+
clearPendingPermissionsForSession(event.sessionID)
|
|
269
|
+
clearPendingQuestionsForSession(event.sessionID)
|
|
207
270
|
sessionMap.set(event.sessionID, {
|
|
208
271
|
sessionID: event.sessionID,
|
|
209
272
|
state: "idle",
|
|
@@ -225,6 +288,8 @@ function pruneIdleSessions(refresh = true) {
|
|
|
225
288
|
if (session.state === "idle" && expiresFrom && now - expiresFrom > IDLE_TTL_MS) {
|
|
226
289
|
sessionMap.delete(sessionID)
|
|
227
290
|
activeToolsBySession.delete(sessionID)
|
|
291
|
+
clearPendingPermissionsForSession(sessionID)
|
|
292
|
+
clearPendingQuestionsForSession(sessionID)
|
|
228
293
|
changed = true
|
|
229
294
|
}
|
|
230
295
|
}
|
|
@@ -259,6 +324,8 @@ function getPetState() {
|
|
|
259
324
|
idleIndex: session.state === "idle" ? idleIndex++ : undefined,
|
|
260
325
|
color: DEFAULT_SESSION_COLORS[index % DEFAULT_SESSION_COLORS.length],
|
|
261
326
|
})),
|
|
327
|
+
permissions: Array.from(pendingPermissionsByRequest.values()),
|
|
328
|
+
questions: Array.from(pendingQuestionsByRequest.values()),
|
|
262
329
|
signals: petSignals,
|
|
263
330
|
}
|
|
264
331
|
}
|
|
@@ -678,6 +745,7 @@ function petHtml3D() {
|
|
|
678
745
|
const colorReleaseSpeed = 90
|
|
679
746
|
const faceMeshes = new Map()
|
|
680
747
|
const glowMeshes = new Map()
|
|
748
|
+
const permissionGlowMeshes = new Map()
|
|
681
749
|
let snapshot = window.__PET_STATE || { sessions: [] }
|
|
682
750
|
let lastFrame = performance.now()
|
|
683
751
|
let rotation = { x: -14, y: -28, z: 0 }
|
|
@@ -723,6 +791,7 @@ function petHtml3D() {
|
|
|
723
791
|
const dragParticleTexture = createDragParticleTexture()
|
|
724
792
|
const faceGeometry = new THREE.PlaneGeometry(0.60, 0.60)
|
|
725
793
|
const glowGeometry = new THREE.PlaneGeometry(1.18, 1.18)
|
|
794
|
+
const permissionGlowGeometry = new THREE.PlaneGeometry(1.62, 1.62)
|
|
726
795
|
const rad = THREE.MathUtils.degToRad
|
|
727
796
|
|
|
728
797
|
function createGlowTexture() {
|
|
@@ -763,6 +832,8 @@ function petHtml3D() {
|
|
|
763
832
|
const dragParticles = []
|
|
764
833
|
const toolParticles = []
|
|
765
834
|
const toolEmitAccumulators = new Map()
|
|
835
|
+
const toolEmissionStates = new Map()
|
|
836
|
+
const toolEmissionHoldMs = 2000
|
|
766
837
|
const faceVectors = {
|
|
767
838
|
front: { position: new THREE.Vector3(0, 0, 0.34), normal: new THREE.Vector3(0, 0, 1) },
|
|
768
839
|
back: { position: new THREE.Vector3(0, 0, -0.34), normal: new THREE.Vector3(0, 0, -1) },
|
|
@@ -855,25 +926,61 @@ function petHtml3D() {
|
|
|
855
926
|
return true
|
|
856
927
|
}
|
|
857
928
|
|
|
858
|
-
function updateToolParticles(sessions, dt) {
|
|
859
|
-
const
|
|
860
|
-
const
|
|
929
|
+
function updateToolParticles(sessions, dt, now) {
|
|
930
|
+
const activeIDs = new Set()
|
|
931
|
+
const emitters = []
|
|
932
|
+
const busyIDs = new Set(
|
|
933
|
+
sessions
|
|
934
|
+
.filter((session) => session?.state === "busy" && typeof session.sessionID === "string")
|
|
935
|
+
.map((session) => session.sessionID),
|
|
936
|
+
)
|
|
937
|
+
|
|
938
|
+
for (const session of sessions) {
|
|
939
|
+
if (session?.state !== "busy" || typeof session.sessionID !== "string" || !session.activeTools?.length) continue
|
|
940
|
+
const currentState = toolEmissionStates.get(session.sessionID)
|
|
941
|
+
const faceName = sessionFaceMap.get(session.sessionID) || currentState?.faceName
|
|
942
|
+
if (!faceName) continue
|
|
943
|
+
const color = sessionColorMap.get(session.sessionID) || currentState?.color || randomSessionGlowColor()
|
|
944
|
+
|
|
945
|
+
activeIDs.add(session.sessionID)
|
|
946
|
+
toolEmissionStates.set(session.sessionID, {
|
|
947
|
+
faceName,
|
|
948
|
+
color,
|
|
949
|
+
holdUntil: now + toolEmissionHoldMs,
|
|
950
|
+
})
|
|
951
|
+
emitters.push({ sessionID: session.sessionID, faceName, color, held: false, holdRemainingMs: toolEmissionHoldMs })
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
for (const [sessionID, state] of Array.from(toolEmissionStates.entries())) {
|
|
955
|
+
if (activeIDs.has(sessionID)) continue
|
|
956
|
+
if (!busyIDs.has(sessionID) || !state?.faceName || state.holdUntil <= now) {
|
|
957
|
+
toolEmissionStates.delete(sessionID)
|
|
958
|
+
toolEmitAccumulators.delete(sessionID)
|
|
959
|
+
continue
|
|
960
|
+
}
|
|
961
|
+
emitters.push({
|
|
962
|
+
sessionID,
|
|
963
|
+
faceName: state.faceName,
|
|
964
|
+
color: state.color || randomSessionGlowColor(),
|
|
965
|
+
held: true,
|
|
966
|
+
holdRemainingMs: state.holdUntil - now,
|
|
967
|
+
})
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const emittingIDs = new Set(emitters.map((emitter) => emitter.sessionID))
|
|
861
971
|
for (const sessionID of Array.from(toolEmitAccumulators.keys())) {
|
|
862
|
-
if (!
|
|
972
|
+
if (!emittingIDs.has(sessionID)) toolEmitAccumulators.delete(sessionID)
|
|
863
973
|
}
|
|
864
974
|
|
|
865
|
-
for (const
|
|
866
|
-
const faceName = sessionFaceMap.get(session.sessionID)
|
|
867
|
-
if (!faceName) continue
|
|
868
|
-
const color = sessionColorMap.get(session.sessionID) || randomSessionGlowColor()
|
|
975
|
+
for (const emitter of emitters) {
|
|
869
976
|
const jitterRate = randomBetween(7.5, 11.5)
|
|
870
|
-
const next = (toolEmitAccumulators.get(
|
|
977
|
+
const next = (toolEmitAccumulators.get(emitter.sessionID) || 0) + dt * jitterRate
|
|
871
978
|
let accumulator = next
|
|
872
979
|
while (accumulator >= 1) {
|
|
873
|
-
if (!emitToolParticle(faceName, color)) break
|
|
980
|
+
if (!emitToolParticle(emitter.faceName, emitter.color)) break
|
|
874
981
|
accumulator -= 1
|
|
875
982
|
}
|
|
876
|
-
toolEmitAccumulators.set(
|
|
983
|
+
toolEmitAccumulators.set(emitter.sessionID, accumulator)
|
|
877
984
|
}
|
|
878
985
|
|
|
879
986
|
let activeCount = 0
|
|
@@ -895,8 +1002,9 @@ function petHtml3D() {
|
|
|
895
1002
|
particle.scale.setScalar(data.size * (1.02 + age * 0.42))
|
|
896
1003
|
particle.material.opacity = Math.min(1, 0.24 + Math.sin(age * Math.PI) * 1.18)
|
|
897
1004
|
}
|
|
898
|
-
|
|
899
|
-
|
|
1005
|
+
const heldSessions = emitters.filter((emitter) => emitter.held).length
|
|
1006
|
+
toolParticleGroup.visible = activeCount > 0 || emitters.length > 0
|
|
1007
|
+
return { activeSessions: activeIDs.size, emittingSessions: emitters.length, heldSessions, activeCount }
|
|
900
1008
|
}
|
|
901
1009
|
|
|
902
1010
|
function activeDragColors() {
|
|
@@ -1198,6 +1306,23 @@ function petHtml3D() {
|
|
|
1198
1306
|
glow.rotation.set(...rotation)
|
|
1199
1307
|
cubeGroup.add(glow)
|
|
1200
1308
|
glowMeshes.set(name, glow)
|
|
1309
|
+
|
|
1310
|
+
const permissionGlow = new THREE.Mesh(
|
|
1311
|
+
permissionGlowGeometry,
|
|
1312
|
+
new THREE.MeshBasicMaterial({
|
|
1313
|
+
map: glowTexture,
|
|
1314
|
+
color: 0xffffff,
|
|
1315
|
+
transparent: true,
|
|
1316
|
+
opacity: 0,
|
|
1317
|
+
blending: THREE.AdditiveBlending,
|
|
1318
|
+
depthWrite: false,
|
|
1319
|
+
side: THREE.DoubleSide,
|
|
1320
|
+
}),
|
|
1321
|
+
)
|
|
1322
|
+
permissionGlow.position.set(...glowPosition.map((value) => value === 0 ? 0 : value * 1.072))
|
|
1323
|
+
permissionGlow.rotation.set(...rotation)
|
|
1324
|
+
cubeGroup.add(permissionGlow)
|
|
1325
|
+
permissionGlowMeshes.set(name, permissionGlow)
|
|
1201
1326
|
}
|
|
1202
1327
|
|
|
1203
1328
|
function magnitude(vector) {
|
|
@@ -1384,6 +1509,37 @@ function petHtml3D() {
|
|
|
1384
1509
|
return active
|
|
1385
1510
|
}
|
|
1386
1511
|
|
|
1512
|
+
function applyPermissionGlowFaces(permissions, now) {
|
|
1513
|
+
const pendingSessionIDs = new Set(
|
|
1514
|
+
(permissions || [])
|
|
1515
|
+
.map((permission) => permission?.sessionID)
|
|
1516
|
+
.filter((sessionID) => typeof sessionID === "string"),
|
|
1517
|
+
)
|
|
1518
|
+
const active = {}
|
|
1519
|
+
const wave = (Math.sin(now * 0.0095) + 1) / 2
|
|
1520
|
+
const pulse = Math.pow(wave, 1.85)
|
|
1521
|
+
|
|
1522
|
+
for (const faceName of faceOrder) {
|
|
1523
|
+
const permissionGlow = permissionGlowMeshes.get(faceName)
|
|
1524
|
+
if (!permissionGlow) continue
|
|
1525
|
+
|
|
1526
|
+
const sessionID = Array.from(pendingSessionIDs).find((id) => sessionFaceMap.get(id) === faceName)
|
|
1527
|
+
if (!sessionID) {
|
|
1528
|
+
permissionGlow.material.opacity = 0
|
|
1529
|
+
permissionGlow.scale.setScalar(1)
|
|
1530
|
+
continue
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
const color = sessionColorMap.get(sessionID) || randomSessionGlowColor()
|
|
1534
|
+
permissionGlow.material.color.setRGB(color.r / 255, color.g / 255, color.b / 255)
|
|
1535
|
+
permissionGlow.material.opacity = 0.14 + pulse * 0.60
|
|
1536
|
+
permissionGlow.scale.setScalar(1.06 + pulse * 0.42)
|
|
1537
|
+
active[faceName] = { sessionID, pulse, pending: true, color: color.name, rgb: [color.r, color.g, color.b] }
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
return active
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1387
1543
|
window.__setPetState = setSnapshot
|
|
1388
1544
|
window.__getPetDebug = () => latestDebug
|
|
1389
1545
|
|
|
@@ -1440,9 +1596,11 @@ function petHtml3D() {
|
|
|
1440
1596
|
const glowB = Math.round(232 + (210 - 232) * glow)
|
|
1441
1597
|
const dragParticles = updateDragParticles(now, frictionHoldLevel, speed, dt)
|
|
1442
1598
|
const busyFaces = syncBusyFaces(sessions, speed)
|
|
1443
|
-
const toolParticles = updateToolParticles(sessions, dt)
|
|
1599
|
+
const toolParticles = updateToolParticles(sessions, dt, now)
|
|
1444
1600
|
processSignals(snapshot.signals || [], now)
|
|
1445
1601
|
const helloFlashes = applyFlashFaces(now)
|
|
1602
|
+
const pendingAttention = [...(snapshot.permissions || []), ...(snapshot.questions || [])]
|
|
1603
|
+
const permissionGlows = applyPermissionGlowFaces(pendingAttention, now)
|
|
1446
1604
|
renderCube()
|
|
1447
1605
|
latestDebug = {
|
|
1448
1606
|
now: Date.now(),
|
|
@@ -1472,6 +1630,7 @@ function petHtml3D() {
|
|
|
1472
1630
|
faceRotations,
|
|
1473
1631
|
busyFaces,
|
|
1474
1632
|
helloFlashes,
|
|
1633
|
+
permissionGlows,
|
|
1475
1634
|
}
|
|
1476
1635
|
requestAnimationFrame(tick)
|
|
1477
1636
|
}
|
package/src/plugin-server.cjs
CHANGED
|
@@ -139,6 +139,75 @@ module.exports = {
|
|
|
139
139
|
},
|
|
140
140
|
|
|
141
141
|
event: async ({ event }) => {
|
|
142
|
+
if (event.type === "permission.asked") {
|
|
143
|
+
const permission = event.properties || {}
|
|
144
|
+
await sendEvent({
|
|
145
|
+
type: "permission.ask",
|
|
146
|
+
message: "opencode is waiting for permission",
|
|
147
|
+
sessionID: permission.sessionID,
|
|
148
|
+
requestID: permission.id,
|
|
149
|
+
permission: permission.permission,
|
|
150
|
+
patterns: permission.patterns,
|
|
151
|
+
metadata: permission.metadata,
|
|
152
|
+
always: permission.always,
|
|
153
|
+
tool: permission.tool,
|
|
154
|
+
source: "opencube-plugin",
|
|
155
|
+
})
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (event.type === "permission.replied") {
|
|
160
|
+
const permission = event.properties || {}
|
|
161
|
+
await sendEvent({
|
|
162
|
+
type: "permission.reply",
|
|
163
|
+
message: "opencode permission was answered",
|
|
164
|
+
sessionID: permission.sessionID,
|
|
165
|
+
requestID: permission.requestID,
|
|
166
|
+
reply: permission.reply,
|
|
167
|
+
source: "opencube-plugin",
|
|
168
|
+
})
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (event.type === "question.asked") {
|
|
173
|
+
const question = event.properties || {}
|
|
174
|
+
await sendEvent({
|
|
175
|
+
type: "question.ask",
|
|
176
|
+
message: "opencode is waiting for a question answer",
|
|
177
|
+
sessionID: question.sessionID,
|
|
178
|
+
requestID: question.id,
|
|
179
|
+
questions: question.questions,
|
|
180
|
+
tool: question.tool,
|
|
181
|
+
source: "opencube-plugin",
|
|
182
|
+
})
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (event.type === "question.replied") {
|
|
187
|
+
const question = event.properties || {}
|
|
188
|
+
await sendEvent({
|
|
189
|
+
type: "question.reply",
|
|
190
|
+
message: "opencode question was answered",
|
|
191
|
+
sessionID: question.sessionID,
|
|
192
|
+
requestID: question.requestID,
|
|
193
|
+
answers: question.answers,
|
|
194
|
+
source: "opencube-plugin",
|
|
195
|
+
})
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (event.type === "question.rejected") {
|
|
200
|
+
const question = event.properties || {}
|
|
201
|
+
await sendEvent({
|
|
202
|
+
type: "question.reject",
|
|
203
|
+
message: "opencode question was rejected",
|
|
204
|
+
sessionID: question.sessionID,
|
|
205
|
+
requestID: question.requestID,
|
|
206
|
+
source: "opencube-plugin",
|
|
207
|
+
})
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
142
211
|
if (event.type !== "session.status") return
|
|
143
212
|
|
|
144
213
|
const sessionID = event.properties?.sessionID
|