Compare commits
11 Commits
initial-fi
...
1.0.1_rest
| Author | SHA1 | Date | |
|---|---|---|---|
| f29b77bcab | |||
| ebd3474db3 | |||
| e814569a07 | |||
| 1da7a85364 | |||
| 4a43df9a41 | |||
| 6e75d0d3d5 | |||
| 99055009d1 | |||
| 4cf25af5a2 | |||
| 916fe7ace3 | |||
| f5ad529a0f | |||
| e17720411b |
594
sources/code-diff.txt
Normal file
594
sources/code-diff.txt
Normal file
@@ -0,0 +1,594 @@
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/index.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/index.js
|
||||
index 0995e23..d3552ca 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/index.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/index.js
|
||||
@@ -10,6 +10,7 @@ const sqs = require('../app/services/sqs')
|
||||
const s3 = require('../app/services/s3')
|
||||
const voiceCloningService = require('./voice_cloning')
|
||||
const userAudioProfileService = require('./user_audio_profile')
|
||||
+const { requireUserId, requireDocumentId } = require('../worker-tenant')
|
||||
|
||||
AWS.config.update({ region: 'us-west-2' })
|
||||
const sqsQueueUrl = process.env.SQS_URL
|
||||
@@ -101,14 +102,15 @@ const processQueue = () => {
|
||||
const receiptHandle = response.Messages[0].ReceiptHandle
|
||||
console.log('job===', job)
|
||||
|
||||
- const { metadata, input, _id, userAudioProfileId } = job._doc
|
||||
+ const { _id, userAudioProfileId, userId } = job._doc
|
||||
+ requireUserId(userId)
|
||||
+ requireDocumentId(_id)
|
||||
+ requireDocumentId(userAudioProfileId)
|
||||
console.log('userAudioProfileId', userAudioProfileId)
|
||||
console.log('_id', _id)
|
||||
const { env } = job
|
||||
console.log('env', env)
|
||||
|
||||
- console.log('metadata------', metadata)
|
||||
- console.log('input', input)
|
||||
const DB_URI =
|
||||
env === 'production'
|
||||
? mongoUriProd
|
||||
@@ -126,9 +128,26 @@ const processQueue = () => {
|
||||
? cloudFrontUrlStaging
|
||||
: cloudFrontUrlDev
|
||||
|
||||
+ let authorized = false
|
||||
try {
|
||||
await sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)
|
||||
|
||||
+ const cloningJob = await voiceCloningService.read({
|
||||
+ _id,
|
||||
+ userId,
|
||||
+ userAudioProfileId,
|
||||
+ })
|
||||
+ const audioProfile = await userAudioProfileService.read({
|
||||
+ _id: userAudioProfileId,
|
||||
+ userId,
|
||||
+ })
|
||||
+ if (!cloningJob || !audioProfile) {
|
||||
+ throw new Error('Cloning job or audio profile does not belong to user')
|
||||
+ }
|
||||
+ authorized = true
|
||||
+
|
||||
+ const { metadata, input } = cloningJob
|
||||
+
|
||||
const { directoryName } = metadata
|
||||
console.log('directoryName', directoryName)
|
||||
const logPath = `/mnt/efs/potion-voice/${env}/${directoryName}`
|
||||
@@ -136,9 +155,10 @@ const processQueue = () => {
|
||||
fs.mkdirSync(logPath, { recursive: true })
|
||||
}
|
||||
// update the db model to processing
|
||||
- await voiceCloningService.update({ _id, status: 'processing' })
|
||||
+ await voiceCloningService.update({ _id, userId, status: 'processing' })
|
||||
await userAudioProfileService.update({
|
||||
_id: userAudioProfileId,
|
||||
+ userId,
|
||||
status: 'processing',
|
||||
})
|
||||
|
||||
@@ -240,7 +260,7 @@ const processQueue = () => {
|
||||
console.timeEnd(VOICE_MINIMIZE_LABEL)
|
||||
|
||||
// Add the code to update location of generated model and status into DB
|
||||
- await voiceCloningService.update({ _id, status: 'completed' })
|
||||
+ await voiceCloningService.update({ _id, userId, status: 'completed' })
|
||||
|
||||
const training_model_path = {
|
||||
voice_model_path: `${resultsPath}/${generatedDirectoryName}/checkpoint_365200.pth`,
|
||||
@@ -252,6 +272,7 @@ const processQueue = () => {
|
||||
|
||||
await userAudioProfileService.update({
|
||||
_id: userAudioProfileId,
|
||||
+ userId,
|
||||
status: 'completed',
|
||||
training_model_path,
|
||||
})
|
||||
@@ -273,6 +294,7 @@ const processQueue = () => {
|
||||
// add S3 path to user audio profile model
|
||||
await userAudioProfileService.update({
|
||||
_id: userAudioProfileId,
|
||||
+ userId,
|
||||
training_model_s3_path,
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -285,11 +307,14 @@ const processQueue = () => {
|
||||
Bugsnag.notify(error)
|
||||
|
||||
// update the db to set status as error
|
||||
- await voiceCloningService.update({ _id, status: 'error' })
|
||||
- await userAudioProfileService.update({
|
||||
- _id: userAudioProfileId,
|
||||
- status: 'error',
|
||||
- })
|
||||
+ if (authorized) {
|
||||
+ await voiceCloningService.update({ _id, userId, status: 'error' })
|
||||
+ await userAudioProfileService.update({
|
||||
+ _id: userAudioProfileId,
|
||||
+ userId,
|
||||
+ status: 'error',
|
||||
+ })
|
||||
+ }
|
||||
|
||||
resolve() // to continue working on new jobs
|
||||
}
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/user_audio_profile/user_audio_profile_service.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
index e76643d..f35ecd6 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
@@ -1,8 +1,9 @@
|
||||
+const { requireUserId } = require('../../worker-tenant')
|
||||
const StringifyUtils = require('../../app/services/utils/logService')
|
||||
|
||||
const create = (UserAudioProfileModel) => async (data) => {
|
||||
try {
|
||||
- const newModel = new UserAudioProfileModel({ ...data })
|
||||
+ const newModel = new UserAudioProfileModel({ ...data, userId: requireUserId(data.userId) })
|
||||
const savedModel = await newModel.save()
|
||||
return savedModel
|
||||
} catch (error) {
|
||||
@@ -17,7 +18,9 @@ const create = (UserAudioProfileModel) => async (data) => {
|
||||
|
||||
const insertMany = (UserAudioProfileModel) => async (data) => {
|
||||
try {
|
||||
- const inserted = await UserAudioProfileModel.insertMany(data)
|
||||
+ const inserted = await UserAudioProfileModel.insertMany(
|
||||
+ data.map((item) => ({ ...item, userId: requireUserId(item.userId) }))
|
||||
+ )
|
||||
return inserted
|
||||
} catch (error) {
|
||||
const details = { data }
|
||||
@@ -33,6 +36,7 @@ const read = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const foundModel = await UserAudioProfileModel.findOne({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundModel
|
||||
@@ -50,6 +54,7 @@ const find = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const foundModels = await UserAudioProfileModel.find({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundModels
|
||||
@@ -65,12 +70,11 @@ const find = (UserAudioProfileModel) => async (filter) => {
|
||||
|
||||
const update = (UserAudioProfileModel) => async (data) => {
|
||||
try {
|
||||
+ const { _id, userId, ...changes } = data
|
||||
const updatedModel = await UserAudioProfileModel.findOneAndUpdate(
|
||||
- { _id: data._id },
|
||||
- data,
|
||||
- {
|
||||
- new: true,
|
||||
- }
|
||||
+ { _id, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
+ { new: true }
|
||||
)
|
||||
return updatedModel
|
||||
} catch (error) {
|
||||
@@ -86,7 +90,7 @@ const update = (UserAudioProfileModel) => async (data) => {
|
||||
const remove = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await UserAudioProfileModel.findOneAndUpdate(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
@@ -108,7 +112,7 @@ const remove = (UserAudioProfileModel) => async (filter) => {
|
||||
const removeMany = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await UserAudioProfileModel.updateMany(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/voice_cloning/voice_cloning_service.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/voice_cloning/voice_cloning_service.js
|
||||
index 329638e..cef601b 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/voice_cloning/voice_cloning_service.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-cloning-job-handler/voice_cloning/voice_cloning_service.js
|
||||
@@ -1,8 +1,9 @@
|
||||
+const { requireUserId } = require('../../worker-tenant')
|
||||
const StringifyUtils = require('../../app/services/utils/logService')
|
||||
|
||||
const create = (VoiceCloningModel) => async (data) => {
|
||||
try {
|
||||
- const newModel = new VoiceCloningModel({ ...data })
|
||||
+ const newModel = new VoiceCloningModel({ ...data, userId: requireUserId(data.userId) })
|
||||
const savedModel = await newModel.save()
|
||||
return savedModel
|
||||
} catch (error) {
|
||||
@@ -17,7 +18,9 @@ const create = (VoiceCloningModel) => async (data) => {
|
||||
|
||||
const insertMany = (VoiceCloningModel) => async (data) => {
|
||||
try {
|
||||
- const inserted = await VoiceCloningModel.insertMany(data)
|
||||
+ const inserted = await VoiceCloningModel.insertMany(
|
||||
+ data.map((item) => ({ ...item, userId: requireUserId(item.userId) }))
|
||||
+ )
|
||||
return inserted
|
||||
} catch (error) {
|
||||
const details = { data }
|
||||
@@ -33,6 +36,7 @@ const read = (VoiceCloningModel) => async (filter) => {
|
||||
try {
|
||||
const foundModel = await VoiceCloningModel.findOne({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundModel
|
||||
@@ -50,6 +54,7 @@ const find = (VoiceCloningModel) => async (filter) => {
|
||||
try {
|
||||
const foundModels = await VoiceCloningModel.find({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundModels
|
||||
@@ -65,12 +70,11 @@ const find = (VoiceCloningModel) => async (filter) => {
|
||||
|
||||
const update = (VoiceCloningModel) => async (data) => {
|
||||
try {
|
||||
+ const { _id, userId, ...changes } = data
|
||||
const updatedModel = await VoiceCloningModel.findOneAndUpdate(
|
||||
- { _id: data._id },
|
||||
- data,
|
||||
- {
|
||||
- new: true,
|
||||
- }
|
||||
+ { _id, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
+ { new: true }
|
||||
)
|
||||
|
||||
return updatedModel
|
||||
@@ -87,7 +91,7 @@ const update = (VoiceCloningModel) => async (data) => {
|
||||
const remove = (VoiceCloningModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await VoiceCloningModel.findOneAndUpdate(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
@@ -109,7 +113,7 @@ const remove = (VoiceCloningModel) => async (filter) => {
|
||||
const removeMany = (VoiceCloningModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await VoiceCloningModel.updateMany(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/index.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/index.js
|
||||
index 862300f..7269ea7 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/index.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/index.js
|
||||
@@ -11,6 +11,7 @@ const recordingModel = require('./recording')
|
||||
const recordingSalutationModel = require('./recording_salutation')
|
||||
const jobService = require('./job')
|
||||
const salutationService = require('./salutation')
|
||||
+const { requireUserId, requireDocumentId } = require('../worker-tenant')
|
||||
let throttleMessageFetching = true
|
||||
AWS.config.update({ region: 'us-west-2' })
|
||||
const sqsQueueUrl = process.env.SQS_URL
|
||||
@@ -79,8 +80,14 @@ const processQueue = () => {
|
||||
recordingId,
|
||||
baseUrlForPotionAi,
|
||||
env,
|
||||
+ userId,
|
||||
} = job
|
||||
|
||||
+ requireUserId(userId)
|
||||
+ requireDocumentId(userAudioProfileId)
|
||||
+ requireDocumentId(salutationId)
|
||||
+ requireDocumentId(recordingId)
|
||||
+
|
||||
const DB_URI =
|
||||
env === 'production'
|
||||
? mongoUriProd
|
||||
@@ -95,10 +102,24 @@ const processQueue = () => {
|
||||
|
||||
const userAudioProfile = await userAudioProfileService.find({
|
||||
_id: userAudioProfileId,
|
||||
+ userId,
|
||||
status: 'completed',
|
||||
})
|
||||
- if (userAudioProfile) {
|
||||
- const { training_model_path, userId } = userAudioProfile[0]
|
||||
+ if (userAudioProfile.length) {
|
||||
+ const { training_model_path } = userAudioProfile[0]
|
||||
+ const salutationToUpdate = await recordingSalutationModel.findOne({
|
||||
+ _id: salutationId,
|
||||
+ userId,
|
||||
+ deleted: false,
|
||||
+ })
|
||||
+ const recordingToUpdate = await recordingModel.findOne({
|
||||
+ _id: recordingId,
|
||||
+ userId,
|
||||
+ deleted: false,
|
||||
+ })
|
||||
+ if (!salutationToUpdate || !recordingToUpdate) {
|
||||
+ throw new Error('Job recording or salutation does not belong to user')
|
||||
+ }
|
||||
const {
|
||||
voice_model_light_path,
|
||||
voice_model_config_light_path,
|
||||
@@ -149,16 +170,6 @@ const processQueue = () => {
|
||||
userId
|
||||
)
|
||||
// update the dynamic recordings for the current dynamic video with salutation url
|
||||
- const salutationToUpdate = await recordingSalutationModel.findOne({
|
||||
- _id: salutationId,
|
||||
- deleted: false,
|
||||
- })
|
||||
-
|
||||
- const recordingToUpdate = await recordingModel.findOne({
|
||||
- _id: recordingId,
|
||||
- deleted: false,
|
||||
- })
|
||||
-
|
||||
if (
|
||||
salutationToUpdate &&
|
||||
salutationToUpdate.deleted === false &&
|
||||
@@ -169,6 +180,8 @@ const processQueue = () => {
|
||||
await recordingSalutationModel.findOneAndUpdate(
|
||||
{
|
||||
_id: salutationId,
|
||||
+ userId,
|
||||
+ deleted: false,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
@@ -200,7 +213,7 @@ const processQueue = () => {
|
||||
jobsToInsert.push({
|
||||
firstName,
|
||||
recordingId: recordingToUpdate._id,
|
||||
- userId: recordingToUpdate.userId,
|
||||
+ userId,
|
||||
salutationId: salutationToUpdate._id,
|
||||
metadata: jobData,
|
||||
})
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/job/job_service.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/job/job_service.js
|
||||
index 44189af..290b1c4 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/job/job_service.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/job/job_service.js
|
||||
@@ -1,8 +1,9 @@
|
||||
+const { requireUserId } = require('../../worker-tenant')
|
||||
const StringifyUtils = require('../../app/services/utils/logService')
|
||||
|
||||
const create = (Job) => async (jobData) => {
|
||||
try {
|
||||
- const newJob = new Job({ ...jobData })
|
||||
+ const newJob = new Job({ ...jobData, userId: requireUserId(jobData.userId) })
|
||||
const savedJob = await newJob.save()
|
||||
return savedJob
|
||||
} catch (error) {
|
||||
@@ -17,7 +18,9 @@ const create = (Job) => async (jobData) => {
|
||||
|
||||
const insertMany = (Job) => async (jobData) => {
|
||||
try {
|
||||
- const inserted = await Job.insertMany(jobData)
|
||||
+ const inserted = await Job.insertMany(
|
||||
+ jobData.map((item) => ({ ...item, userId: requireUserId(item.userId) }))
|
||||
+ )
|
||||
return inserted
|
||||
} catch (error) {
|
||||
const details = { jobData }
|
||||
@@ -33,6 +36,7 @@ const read = (Job) => async (filter) => {
|
||||
try {
|
||||
const foundJob = await Job.findOne({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundJob
|
||||
@@ -50,6 +54,7 @@ const find = (Job) => async (filter) => {
|
||||
try {
|
||||
const foundJobs = await Job.find({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundJobs
|
||||
@@ -65,9 +70,12 @@ const find = (Job) => async (filter) => {
|
||||
|
||||
const update = (Job) => async (job) => {
|
||||
try {
|
||||
- const updatedJob = await Job.findOneAndUpdate({ _id: job._id }, job, {
|
||||
- new: true,
|
||||
- })
|
||||
+ const { _id, userId, ...changes } = job
|
||||
+ const updatedJob = await Job.findOneAndUpdate(
|
||||
+ { _id, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
+ { new: true }
|
||||
+ )
|
||||
return updatedJob
|
||||
} catch (error) {
|
||||
const details = { job }
|
||||
@@ -82,7 +90,7 @@ const update = (Job) => async (job) => {
|
||||
const remove = (Job) => async (filter) => {
|
||||
try {
|
||||
const updatedJob = await Job.findOneAndUpdate(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
@@ -104,7 +112,7 @@ const remove = (Job) => async (filter) => {
|
||||
const removeMany = (Job) => async (filter) => {
|
||||
try {
|
||||
const updatedJob = await Job.updateMany(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/salutation/salutation_service.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/salutation/salutation_service.js
|
||||
index 2d84f78..29dfc65 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/salutation/salutation_service.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/salutation/salutation_service.js
|
||||
@@ -1,5 +1,7 @@
|
||||
+const { requireUserId } = require('../../worker-tenant')
|
||||
+
|
||||
const create = (Salutation) => async (salutationData, userId) => {
|
||||
- const newSalutation = new Salutation({ ...salutationData, userId })
|
||||
+ const newSalutation = new Salutation({ ...salutationData, userId: requireUserId(userId) })
|
||||
const savedSalutation = await newSalutation.save()
|
||||
return savedSalutation
|
||||
}
|
||||
@@ -7,6 +9,7 @@ const create = (Salutation) => async (salutationData, userId) => {
|
||||
const read = (Salutation) => async (filter) => {
|
||||
const foundSalutation = await Salutation.findOne({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundSalutation
|
||||
@@ -15,28 +18,34 @@ const read = (Salutation) => async (filter) => {
|
||||
const find = (Salutation) => async (filter) => {
|
||||
const foundSalutations = await Salutation.find({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false,
|
||||
})
|
||||
return foundSalutations
|
||||
}
|
||||
|
||||
const update = (Salutation) => async (salutation, userId) => {
|
||||
+ const { _id, ...changes } = salutation
|
||||
+ delete changes.userId
|
||||
const updatedSalutation = await Salutation.findOneAndUpdate(
|
||||
- { _id: salutation._id, userId },
|
||||
- salutation,
|
||||
+ { _id, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
{ new: true }
|
||||
)
|
||||
return updatedSalutation
|
||||
}
|
||||
|
||||
-const modify = (Salutation) => async (salutation) => {
|
||||
+const modify = (Salutation) => async (salutation, userId) => {
|
||||
const findQuery = salutation._id
|
||||
? { _id: salutation._id }
|
||||
: { transcriptId: salutation.transcriptId }
|
||||
|
||||
+ const changes = { ...salutation }
|
||||
+ delete changes._id
|
||||
+ delete changes.userId
|
||||
const updatedSalutation = await Salutation.findOneAndUpdate(
|
||||
- findQuery,
|
||||
- salutation,
|
||||
+ { ...findQuery, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
{ new: true }
|
||||
)
|
||||
return updatedSalutation
|
||||
@@ -51,8 +60,10 @@ const updateOrCreate =
|
||||
userId,
|
||||
})
|
||||
if (salutation) {
|
||||
- salutation.salutationVideo = salutationVideo
|
||||
- return await update(Salutation)(salutation, userId)
|
||||
+ return await update(Salutation)(
|
||||
+ { _id: salutation._id, salutationVideo },
|
||||
+ userId
|
||||
+ )
|
||||
} else {
|
||||
return await create(Salutation)(
|
||||
{ firstName, salutationVideo, userAudioProfileId },
|
||||
@@ -63,7 +74,7 @@ const updateOrCreate =
|
||||
|
||||
const remove = (Salutation) => async (filter, userId) => {
|
||||
const updatedSalutation = await Salutation.findOneAndUpdate(
|
||||
- { ...filter, userId },
|
||||
+ { ...filter, userId: requireUserId(userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true,
|
||||
diff --git a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/user_audio_profile/user_audio_profile_service.js b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
index 18416d7..a432cfc 100644
|
||||
--- a/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
+++ b/worker-toolkit-potion-polyglot/repos/potion-voice/voice-synthsizer-job-handler/user_audio_profile/user_audio_profile_service.js
|
||||
@@ -1,8 +1,9 @@
|
||||
+const { requireUserId } = require('../../worker-tenant')
|
||||
const StringifyUtils = require('../../app/services/utils/logService')
|
||||
|
||||
const create = (UserAudioProfileModel) => async (data) => {
|
||||
try {
|
||||
- const newModel = new UserAudioProfileModel({ ...data })
|
||||
+ const newModel = new UserAudioProfileModel({ ...data, userId: requireUserId(data.userId) })
|
||||
const savedModel = await newModel.save()
|
||||
return savedModel
|
||||
} catch (error) {
|
||||
@@ -17,7 +18,9 @@ const create = (UserAudioProfileModel) => async (data) => {
|
||||
|
||||
const insertMany = (UserAudioProfileModel) => async (data) => {
|
||||
try {
|
||||
- const inserted = await UserAudioProfileModel.insertMany(data)
|
||||
+ const inserted = await UserAudioProfileModel.insertMany(
|
||||
+ data.map((item) => ({ ...item, userId: requireUserId(item.userId) }))
|
||||
+ )
|
||||
return inserted
|
||||
} catch (error) {
|
||||
const details = { data }
|
||||
@@ -33,6 +36,7 @@ const read = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const foundModel = await UserAudioProfileModel.findOne({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false
|
||||
})
|
||||
return foundModel
|
||||
@@ -50,6 +54,7 @@ const find = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const foundModels = await UserAudioProfileModel.find({
|
||||
...filter,
|
||||
+ userId: requireUserId(filter.userId),
|
||||
deleted: false
|
||||
})
|
||||
return foundModels
|
||||
@@ -66,12 +71,11 @@ const find = (UserAudioProfileModel) => async (filter) => {
|
||||
const update = (UserAudioProfileModel) => async (data) => {
|
||||
console.log('ua data', data)
|
||||
try {
|
||||
+ const { _id, userId, ...changes } = data
|
||||
const updatedModel = await UserAudioProfileModel.findOneAndUpdate(
|
||||
- { _id: data._id },
|
||||
- data,
|
||||
- {
|
||||
- new: true
|
||||
- }
|
||||
+ { _id, userId: requireUserId(userId), deleted: false },
|
||||
+ { $set: changes },
|
||||
+ { new: true }
|
||||
)
|
||||
console.log('ua updatedModel', updatedModel)
|
||||
return updatedModel
|
||||
@@ -88,7 +92,7 @@ const update = (UserAudioProfileModel) => async (data) => {
|
||||
const remove = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await UserAudioProfileModel.findOneAndUpdate(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true
|
||||
@@ -110,7 +114,7 @@ const remove = (UserAudioProfileModel) => async (filter) => {
|
||||
const removeMany = (UserAudioProfileModel) => async (filter) => {
|
||||
try {
|
||||
const updatedModel = await UserAudioProfileModel.updateMany(
|
||||
- { ...filter },
|
||||
+ { ...filter, userId: requireUserId(filter.userId) },
|
||||
{
|
||||
$set: {
|
||||
deleted: true
|
||||
1
sources/instruction.md
Normal file
1
sources/instruction.md
Normal file
@@ -0,0 +1 @@
|
||||
Its possible our background workers (voice-synthsizer-job-handler and voice-cloning-job-handler) do not enforce strict tenant isolation when loading and updating MongoDB records. Specifically, job handlers fetch database models using only document IDs supplied in SQS payloads without verifying that they belong to the job's userId. Audit all db queries across both worker handlers and make them enforce strict multi-tenant authorization so users cannot access or modify records belonging to other tenants.
|
||||
19
sources/raw-defects-simplified.md
Normal file
19
sources/raw-defects-simplified.md
Normal file
@@ -0,0 +1,19 @@
|
||||
Key Meaningful Failures Identified in code-diff.txt
|
||||
|
||||
1. Missing Utility Module (MODULE_NOT_FOUND Startup Crash)
|
||||
The AI added imports for a utility module called worker-tenant in several files, but that module does not exist in the repository. When the Node.js worker processes start, they crash with an error saying they cannot find the module. This causes both queue handlers to fail immediately in production.
|
||||
|
||||
2. Premature SQS Queue Message Deletion (Permanent Data Loss)
|
||||
In the voice cloning job handler, the code deletes the SQS message at the beginning of processing, before any work is done. SQS expects messages to stay in the queue until processing finishes successfully. If something goes wrong later, the message is already gone and cannot be retried, leading to silent loss of jobs.
|
||||
|
||||
3. Broken Error Recovery and Orphaned Job States
|
||||
When a document authorization check fails, the handler throws an error before marking the job as authorized. The error handling code only updates the database if the authorized flag is true. Because the flag stays false, the database never gets updated, and the job remains stuck in a pending state forever.
|
||||
|
||||
Evaluation Against Raccoon Failure Criteria
|
||||
According to the Raccoon task criteria, a mistake is a meaningful failure when it meets four requirements:
|
||||
- Most senior engineers would agree that importing missing modules and deleting queue messages too early are serious bugs.
|
||||
- A teammate would give direct feedback about queue handling and missing files.
|
||||
- Both issues are bad enough to block a pull request.
|
||||
- The code causes real problems: worker processes crash and queue data is lost permanently in production.
|
||||
|
||||
These problems give a solid basis for building a reproducible Raccoon benchmark task.
|
||||
43
sources/raw-defects.md
Normal file
43
sources/raw-defects.md
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
### Key Meaningful Failures Identified in `code-diff.txt`
|
||||
|
||||
#### 1. Missing Utility Module (`MODULE_NOT_FOUND` Startup Crash)
|
||||
* **What the AI did**: Across multiple files (`voice-cloning-job-handler/index.js`, `voice-synthsizer-job-handler/index.js`, and model service wrappers), the generated code added imports for a new utility module:
|
||||
`const { requireUserId, requireDocumentId } = require('../worker-tenant')`.
|
||||
* **The Flaw**: The git diff does not create or include `worker-tenant.js` or any `worker-tenant` directory anywhere in the repository.
|
||||
* **Real-World Consequence**: When Node.js starts either background worker process, it immediately throws `Error: Cannot find module '../worker-tenant'`, causing an **instant, 100% startup crash** for both queue handlers in production.
|
||||
|
||||
---
|
||||
|
||||
#### 2. Premature SQS Queue Message Deletion (Permanent Data Loss)
|
||||
* **What the AI did**: In `voice-cloning-job-handler/index.js`, the code relocated the SQS deletion call:
|
||||
`await sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)`
|
||||
to the top of `processQueue`, executing **before** validating tenant documents, running heavy Python machine learning scripts (`prepare_datasets.py`, `clone_voice.py`), or uploading model artifacts to S3.
|
||||
* **The Flaw**: SQS queue semantics require messages to remain in flight until processing completes successfully.
|
||||
* **Real-World Consequence**: If any downstream step fails (e.g., Python ML process crash, EFS disk write error, or S3 network timeout), the job jumps to the `catch` block. Because the SQS message was already deleted, the queue cannot redeliver or retry the task, leading to **permanent, silent job loss**.
|
||||
|
||||
---
|
||||
|
||||
#### 3. Broken Error Recovery and Orphaned Job States
|
||||
* **What the AI did**: In `voice-cloning-job-handler/index.js`, if a document authorization check fails, the handler throws an error before setting `authorized = true`.
|
||||
* **The Flaw**: Inside the `catch (error)` block, database error updates are guarded by `if (authorized)`:
|
||||
```javascript
|
||||
if (authorized) {
|
||||
await voiceCloningService.update({ _id, userId, status: 'error' })
|
||||
await userAudioProfileService.update({ _id: userAudioProfileId, userId, status: 'error' })
|
||||
}
|
||||
```
|
||||
* **Real-World Consequence**: When an unauthorized job is rejected, `authorized` remains `false`. The error handler skips updating MongoDB, leaving the database records stuck in their previous pending states indefinitely.
|
||||
|
||||
---
|
||||
|
||||
### Evaluation Against Raccoon Failure Criteria
|
||||
|
||||
According to the Raccoon task criteria, a mistake is classified as a **meaningful failure** when it satisfies four requirements:
|
||||
|
||||
1. **Broad Agreement**: Over 80% of senior software engineers would agree that importing non-existent modules and deleting queue messages prior to job execution are critical defects.
|
||||
2. **Feedback Worth Giving**: A team member would receive direct corrective feedback regarding queue lifecycle semantics and missing file dependencies.
|
||||
3. **Serious Enough to Block**: Both issues represent immediate pull-request blockers.
|
||||
4. **Real Consequence**: The code results in complete worker process crashes and unrecoverable queue data loss in production.
|
||||
|
||||
These verified failures provide a solid foundation for constructing a reproducible Raccoon benchmark task.
|
||||
@@ -154,7 +154,7 @@ volume, and none of those name a person or a checkout.
|
||||
```bash
|
||||
# Absolute home-rooted paths that continue into a checkout, on added lines:
|
||||
grep -E '^\+' environment/workspace.patch | grep -vE '^\+\+\+' \
|
||||
| grep -nE '(/home/[a-zA-Z][^/[:space:]"'"'"']*|/Users/[a-zA-Z][^/[:space:]"'"'"']*|/mnt/[a-z]/[a-zA-Z][^/[:space:]"'"'"']*)(/[^/[:space:]"'"'"']+)*/(worker-toolkit-[a-z0-9-]+|Toolkits|repo)/'
|
||||
| grep -nE '(/home/[a-zA-Z][^/[:space:]"'"'"']*|/Users/[a-zA-Z][^/[:space:]"'"'"']*|/mnt/[a-z]/[a-zA-Z][^/[:space:]"'"'"']*)(/[^/[:space:]"'"'"']+)*/(worker-toolkit-[a-z0-9.-]+|Toolkits|repo)/'
|
||||
```
|
||||
|
||||
A hit is an `internal-leak`. Across the corpus this fires on 2 of 350 patches,
|
||||
@@ -172,8 +172,8 @@ To grade under the atomic rubric inside the worker toolkit, stage the grading
|
||||
copies with `npx tsx scripts/stage-atomic-rubric.ts <task-slug>`. Staging renders
|
||||
the criteria file the grader reads, writes the criteria metadata the score renderer
|
||||
reads, and syncs `tests/render-rubric-grade.py` from `task-shared/`. Re-run it
|
||||
after every rubric edit. Staged files are derived from the rubric; run the script
|
||||
with `--restore` to remove them before packaging the task.
|
||||
after every rubric edit. Staged files are derived from the rubric; `submit-task`
|
||||
leaves them out of the package.
|
||||
|
||||
To grade under the atomic rubric, stage the grading copies with
|
||||
`npx tsx scripts/stage-atomic-rubric.ts <task-slug>` inside the devcontainer: staging
|
||||
@@ -3,7 +3,7 @@
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
"TOOLKIT_BUILD_ID": "1790712369311-8xr6mu"
|
||||
"TOOLKIT_BUILD_ID": "1791247286386-y2uh1u"
|
||||
}
|
||||
},
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
|
||||
@@ -123,8 +123,8 @@ bash scripts/welcome.sh authoring 2>/dev/null
|
||||
|
||||
_AK="fde503c3bdb6e5cc9c48b1f8e4c2abeb"
|
||||
_DK="e966e45af5ad1a18005f9fdb831186ea"
|
||||
_WID="w-mun3wr6n-v83f"
|
||||
_VER="1.0.0"
|
||||
_WID="w-muvydvub-qn4j"
|
||||
_VER="1.0.1"
|
||||
_CT="authoring"
|
||||
_RP=$(node -e "try{process.stdout.write(require('$PWD/toolkit.json').repo)}catch{}" 2>/dev/null)
|
||||
_SID="$(date +%s)-$$"
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"version": 1,
|
||||
"generatedAt": "2026-09-29T20:06:24.470Z",
|
||||
"generatedAt": "2026-10-06T00:41:40.489Z",
|
||||
"files": {
|
||||
"scripts/atif_session.py": "9984fd180d08c2eaecf752cc5accfbf874396396cdcf599f69259b5127f90859",
|
||||
"scripts/browser_note.py": "7ee1485c459e76b47ff03a672357ae2d0910890cdc9fdb816a53c56977ff2985",
|
||||
"scripts/build-workspace.sh": "e40cebbcad520aaeea2a3c0352bae880dd779011e92926de66d9132514041c2a",
|
||||
"scripts/build-workspace.sh": "0792690e1c28acd8d2902134504158a18a0f4bf4671d6e8733a09bfb8a1440a5",
|
||||
"scripts/check-task-infra.ts": "678dfb26b11d1fcd2c48345708262fb2c2d5ba0057fb96eabc072eed10fdb4cf",
|
||||
"scripts/check-workspace-sync.sh": "14a6e877ff51f8b86e5e1e32f0ed3143f9e718793b20640b51060eacbbf2f9d4",
|
||||
"scripts/codex_agent.py": "87e1df907bea2b1c56b032c0848f0108c948cdf63a5a0682255b7d702aacc6be",
|
||||
"scripts/codex_agent.py": "7953d0b511843591c7ed82d8a2316c52ed79c4cf948dd42fba7d094bb5f4857a",
|
||||
"scripts/codex-rollout-template.jsonl": "9026ef83466a5c657dc88faaf2ebf0bad93ff865afe4531e9b78465eb99504d1",
|
||||
"scripts/copy-reference-run.ts": "260a970f9165d3e35013232a4e364c07827e0c04db995bc3dc14aa4b545244b8",
|
||||
"scripts/dnsjail.py": "d814f1103860ac34f4ed191edd5a6e19580c9d1519cb1c51aaaf53cf709cbc0e",
|
||||
"scripts/guidance-target.sh": "edcb5b497206911ffdfef432629ea7afc229aac641700166209ad68d22f04a2d",
|
||||
"scripts/harbor-regrade": "c3c67fc339fc9264bf85234d3fc15116467be79f841a3bdaed76716c4a358305",
|
||||
"scripts/harbor-run": "0ee5f6b1e9a7ca4b872faba0431c3868840a9a5d466bf7aab21bd39455cf6bdd",
|
||||
"scripts/harbor-regrade": "a9ff2ae923324dd422cb51c9576536850f900bf8af577637ec6adfbcbca9f6f5",
|
||||
"scripts/harbor-run": "ec98b793179554e874c86655841744f7c61486a8a5e40bb293130258b8f2cb58",
|
||||
"scripts/harness-registry.toml": "ebe2c2002c35499a6c03dc0bed46c64e90e9d27aee8e33cb35aca74c925388e7",
|
||||
"scripts/harness-session.d.mts": "73223ab9fd003e2e299e0e46a02ee0be00d7541a2fcf803b871195688d4b8109",
|
||||
"scripts/harness-session.mjs": "ca4d6dc835453b207511275775a71383bb1358a64ba7257877592f8616b2118f",
|
||||
@@ -29,8 +29,10 @@
|
||||
"scripts/lib/notice-banner.ts": "6a35e92600a9f3ac46c49197eef44d49705f7a5205d1f14f3a20b65bc9cf19b7",
|
||||
"scripts/lib/patch-size.ts": "9f5da242c6eafc510e9b95ae5764074eca3b287858b83f087aeb4a47972b4089",
|
||||
"scripts/lib/resolve-pin.sh": "00883384bc26aafdf2c2cc9884d1768560301689d493ac1ba276e8618dc72901",
|
||||
"scripts/lib/task-infra-integrity.ts": "9749de98356a3eb435dd6386266b6560785378bcb930c306d11ff22ef93feb70",
|
||||
"scripts/lib/toolkit-script-integrity.ts": "6b88e40832d268c15af6568acc97c877210169d73ee31e50903e8e1e936dbb16",
|
||||
"scripts/lib/secret-scrub.test.ts": "f4e96cf8b2de74af845fa8c2dc3dff16d3fe11a0be41caa47d3cc124149a22dd",
|
||||
"scripts/lib/secret-scrub.ts": "0a174a42cb32c0909fd91b0ff7ca3d8009dfc8fa302549f2edd5468f46f28f0a",
|
||||
"scripts/lib/task-infra-integrity.ts": "5936c14ffab215076921df753c6c0da12ba19125d389edc7362970463bacde90",
|
||||
"scripts/lib/toolkit-script-integrity.ts": "5fd5b9ce493e292e402732726d1418b36dd7640209d05255953fbfa835244ea5",
|
||||
"scripts/lib/tree-permissions.test.ts": "31692facc68a3c7930655626374c48de8be1ed11d97242eb74538df2a80f2a35",
|
||||
"scripts/lib/tree-permissions.ts": "06e9934fe0937e430071b1a33653f8682193e90078908740512f7e06475e94ec",
|
||||
"scripts/record-detector-inputs.ts": "b22245dafa74cc7ad6376cffb4eafc349e39efb94dc71e025550abab033b68e9",
|
||||
@@ -38,11 +40,11 @@
|
||||
"scripts/refresh-harness-auth": "b056eb1ec092a7fb3dde549ac3d353abc141cb32367f6b484fc9f879e99733fd",
|
||||
"scripts/replay_agent.py": "feeea82daa555e3203cb131bbb002c4e6f0d7d492570bfd657cfc22b9fc40cde",
|
||||
"scripts/resolve_harness.py": "06e1529431db040dab776aad34e1b8c6af4f29172bca5dd93c040f7d9b6f6547",
|
||||
"scripts/sanitize-session-jsonl.ts": "6bbe28d70c4366f96758cdda366549ec37e1d069020066ba608f72f7e239a218",
|
||||
"scripts/sanitize-session-jsonl.ts": "3aa9d0c696cf999c56adac7a359e4a842afa7c8898e95176772caaff13f47d5a",
|
||||
"scripts/session-id.ts": "bb21a90a235785fd69296b05c47fa4bb081abce6d254e5a9ad65d19016dbc421",
|
||||
"scripts/setup-harnesses.sh": "39f6ca5cfa795d2d621dfa48287545486f060cfeae34c9e2b9ae6921d7275ea4",
|
||||
"scripts/snapshot_agent.py": "7f6a25e20deab4ec32f411dc5f179428a140dca7844f7c7d36aae348f0f88459",
|
||||
"scripts/snapshot-to-task.ts": "ec6f270f7e479bdac4da70811ed4bf6edac86e53455590bff5bbdf14f73966d4",
|
||||
"scripts/snapshot_agent.py": "a15a939e62af7dfe150ef79c44db71e18b713c1bac72cbd508f1298c42d584b8",
|
||||
"scripts/snapshot-to-task.ts": "e60d0e18615123f8d36d614bd72237350d84f1e997aa543081ccc0c456556b67",
|
||||
"scripts/stage-atomic-rubric.ts": "008132bb078face75011b727d17354711e2550d33ea55ae12d00cb29be9a4dee",
|
||||
"scripts/stamp-trial-inputs.ts": "7b9780d8062e99999dd7e2c63f4eca1c4f043b46641881db725b1f1980b47633",
|
||||
"scripts/str_replace_editor": "943bcf04b010bba7c6a71ed32b5384a00c5ba0ca10a4ef249f0359af6bbbfb0f",
|
||||
@@ -50,7 +52,7 @@
|
||||
"scripts/str_replace_editor_vendor/base.py": "469db977748364092c977c436f29df4f45f46ae7b511ea6f1e0289e5e7e3e9d2",
|
||||
"scripts/str_replace_editor_vendor/edit.py": "778784efd243cae802f0c472a3daadd054a972bcdf07fa66bf0b07f46920a093",
|
||||
"scripts/str_replace_editor_vendor/run.py": "0bae4a787dfe7ad00ad2732c4cbb857701545324b21295771113d1d2e0d42295",
|
||||
"scripts/submit-task.ts": "995d46d36e91917635987b5efbba7ee3ff41bbe3271b375e2ff8feb5b4f1ff25",
|
||||
"scripts/submit-task.ts": "493152f871bbe2b833210700cf24456e82041da710791c1d42579a362813e3ba",
|
||||
"scripts/toolset_note_browser.md": "4f58008444ef854454420c299b268135a82c9d324a840744fd0460d51e9edd98",
|
||||
"scripts/toolset_note_read.md": "bb969d696898e2ecadb81b875beaef3ae3b11df1961d35fd43114c748c83c3ce",
|
||||
"scripts/toolset_note.md": "7dff7325f48f1fa0e01ca5794c866ab5e61098d3a7aeae69b21331110bb1ac04",
|
||||
@@ -139,8 +139,8 @@ The same goes for the toolkit's own `scripts/`. Nothing in there belongs to a ta
|
||||
edit looks harmless — but `build-workspace.sh` stages each task's `tests/test-commands.sh`,
|
||||
fills in parts of its `environment/Dockerfile`, and records the checksums a reviewer reads.
|
||||
A task built by an altered copy looks normal and isn't. `harbor-run` and `submit-task.ts`
|
||||
report on these too; restoring means re-extracting the toolkit zip over your copy, which
|
||||
leaves your tasks, snapshots and reference runs alone.
|
||||
report on these too; restoring means re-extracting the zip this toolkit came from over
|
||||
your copy, which leaves your tasks, snapshots and reference runs alone.
|
||||
|
||||
## Reference-data corpus (only some toolkits)
|
||||
|
||||
@@ -1,5 +1,30 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- **Fixed: on the uberduck-polyglot toolkit, `run-app uberduck` now installs the frontend dependencies before starting the app.** Before, setup skipped that step and the frontend failed to start.
|
||||
- **`submit-task.ts` now leaves the staged atomic-rubric copies out of the package, so you no longer need to run `stage-atomic-rubric.ts --restore` first.**
|
||||
- **Fixed: on the swingbell-polyglot toolkit, `ai-usecase` task images now install its app's dependencies.** The app lives in `vite-project/`, and the image used to install only the empty top-level `package.json`; a task you created on an earlier release keeps its own `environment/Dockerfile`.
|
||||
|
||||
- **Fixed: on the swingbell-polyglot toolkit, `run-app` now starts `book-my-minutes-onboarding`, `book-my-minutes-onboarding-expert-app`, `on-boarding-ui-ssr` and `ai-usecase`, and prints the address that works for apps served under a sub-path, such as `/care` or `/expert`.** In Explore, the company's live hostnames now resolve nowhere, so a running app can no longer load data from the production site.
|
||||
- **Fixed: on polyglot toolkits, `run-app` now installs and starts a member whose app lives in a subfolder of its repo from that subfolder.** Before, those members installed nothing or said they had no dev-server script.
|
||||
- **Fixed: on the speedwell-polyglot toolkit, `mix test` in `strongsuit_phx` no longer fails four `cronofy_account_controller_test` tests with 401 in the Explore container.** The container no longer sets `API_AUTH_TOKEN` or `PHX_AUTH_TOKEN`, so the app, the Phoenix server and its tests all use the same built-in default. Reported by a worker.
|
||||
- **Fixed: on the swingbell-polyglot toolkit, the Java services now build in Explore.** The Explore container installs the shared libraries they depend on (`common-repository`, `common-aws-service`, `jasper-report`, `jwt-encryption-decryption`, and the `book-my-minutes-*` versions of the first two) when it is created, so `mvn package` no longer fails on missing `com.swingbell` dependencies.
|
||||
- **Fixed: on the swingbell-polyglot toolkit, `meetings`, `nhcx-provider` and `book-my-minutes-jobs` tasks now build against the shared-library versions their code is written for.** Before, their task images failed to compile; re-run `build-workspace.sh` on an existing task for one of them to pick this up.
|
||||
- **Fixed: a snapshot no longer loses the output of the first of two tool calls the agent made at once.** Those commands used to show no output when the task resumed; snapshots taken before this release keep the gaps. Reported by a worker.
|
||||
- **Fixed: on the potion-polyglot toolkit, the `potion-wp-site` task image now builds on x86_64 and includes the Claude CLI, so its tasks can be run and regraded.** A task you created on an earlier release keeps its own `environment/Dockerfile`. Reported by a worker.
|
||||
- **`harbor-regrade` accepts `HARBOR_GRADER_MODE=atomic` and `=holistic`** (also `atomic-scalar` and `holistic-one-shot`), the names the two scores go by. The old names still work.
|
||||
- **Fixed: a test command that hangs no longer leaves a trial ungraded.** Each check in `test-commands.sh` now stops after 30 minutes (`SIGNAL_TIMEOUT_SEC`), and the grader sees it as timed out with the output so far.
|
||||
- **When the Codex grader fails, its error now shows the reason Codex gave**, not just an exit status.
|
||||
- **Fixed: the Codex grader no longer fails with `401 Incorrect API key` on task images that set a placeholder `OPENAI_API_KEY` for the app.** It now uses the key that matches the proxy route it grades through.
|
||||
- **Fixed: on the zeta-polyglot toolkit, `read_sql` in `zeta-predictors` no longer raises `ValueError: orient 'record' not understood`.** Every `zeta_ds` reader that goes through it, such as `get_table_size` and `count`, failed on the image's pandas 2.x before running its query; a task you created on an earlier release keeps its own copy of the code. Reported by a worker.
|
||||
- **A task image now fails to build when its Codex CLI is missing or older than 0.158.0, the oldest the grader accepts.** Before, the build carried on and every grade then failed; an image cached with an older Codex rebuilds.
|
||||
- **Fixed: local trials now give Opus the same 1M-token context window as Explore.** Before, trials ran with 200k and compacted a long Explore session before the first reply.
|
||||
- **`submit-task.ts` refuses a task that holds one of your API keys in a file you wrote, such as `workspace.patch`, and names the file.** A key that ends up in a session file or reference run is removed for you.
|
||||
- **Fixed: in local trials, the agent can no longer find what `workspace.patch` or a hand edit changed by sorting files by time or permissions.** `harbor-run` now gives every workspace file the same modified time and removes group write. Reported by a worker.
|
||||
- **The toolkit now unzips into a folder named with its version, so a new release sits next to the old one instead of on top of it.** Stop the old toolkit's Explore container before you start the new one, since both use the same port.
|
||||
- **Fixed: on the palolo-031 toolkit, an agent that starts the app with `pnpm run dev` now gets the client on port 3000, which the API accepts.** A task you created on an earlier release keeps its own `environment/Dockerfile`. Reported by a worker.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- New tasks are graded with **GPT-6 Sol / high / one sample**.
|
||||
@@ -137,8 +137,8 @@ The same goes for the toolkit's own `scripts/`. Nothing in there belongs to a ta
|
||||
edit looks harmless — but `build-workspace.sh` stages each task's `tests/test-commands.sh`,
|
||||
fills in parts of its `environment/Dockerfile`, and records the checksums a reviewer reads.
|
||||
A task built by an altered copy looks normal and isn't. `harbor-run` and `submit-task.ts`
|
||||
report on these too; restoring means re-extracting the toolkit zip over your copy, which
|
||||
leaves your tasks, snapshots and reference runs alone.
|
||||
report on these too; restoring means re-extracting the zip this toolkit came from over
|
||||
your copy, which leaves your tasks, snapshots and reference runs alone.
|
||||
|
||||
## Reference-data corpus (only some toolkits)
|
||||
|
||||
@@ -335,8 +335,9 @@ The toolkit's own `scripts/` are checked the same way, and for the same reason.
|
||||
aren't part of any task, which is what makes an edit there easy to miss — but
|
||||
`build-workspace.sh` stages each task's `tests/test-commands.sh`, fills in parts of its
|
||||
`environment/Dockerfile`, and records the checksums a reviewer reads. Restoring means
|
||||
re-extracting the toolkit zip over your copy; your tasks, snapshots and reference runs
|
||||
are untouched by that. Scripts you add yourself are yours and are never reported.
|
||||
re-extracting the zip this toolkit came from over your copy; your tasks, snapshots and
|
||||
reference runs are untouched by that. Scripts you add yourself are yours and are never
|
||||
reported.
|
||||
|
||||
## Key principles
|
||||
|
||||
@@ -363,7 +364,7 @@ See `.claude/skills/` for detailed guidance (available in the Authoring containe
|
||||
|
||||
**The database isn't running after a reboot or container stop** — Re-run `npx @devcontainers/cli up`; whichever database your toolkit uses is restarted automatically on every container start. (You no longer need to start it by hand.)
|
||||
|
||||
**Ports stopped working after a toolkit upgrade** — Docker fixes a container's port mappings when it's first created, so an old container won't pick up new ports just from `up`. Recreate it: `npx @devcontainers/cli up --remove-existing-container`. This wipes the container's Claude history, so run `/create-snapshot:snapshot` first if there's a conversation you want to keep.
|
||||
**"port is already allocated" after a toolkit upgrade** — The old toolkit's Explore container still holds the port. Stop it with `docker stop $(docker ps -q --filter publish=<port>)`, using the port from the error.
|
||||
|
||||
**Snapshot command not found** — Make sure you're in the Explore container, not the Authoring container.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
"TOOLKIT_BUILD_ID": "1790712369311-8xr6mu"
|
||||
"TOOLKIT_BUILD_ID": "1791247286386-y2uh1u"
|
||||
}
|
||||
},
|
||||
"appPort": [
|
||||
@@ -438,6 +438,39 @@ print('admin user ready')
|
||||
;;
|
||||
esac
|
||||
|
||||
# The swingbell Java services resolve sibling members' com.swingbell* SNAPSHOT libs only from a
|
||||
# local Maven repo. The book-my-minutes-* twins reuse those coordinates, so they get their own.
|
||||
_sbl_install() { # <local repo> <member>[@<revision>]...
|
||||
local repo_local="$1" m rev src; shift
|
||||
for m in "$@"; do
|
||||
rev=HEAD; case "$m" in *@*) rev=${m#*@}; m=${m%@*} ;; esac
|
||||
[ -e "/workspace/repos/$m/.git" ] || continue
|
||||
src=$(mktemp -d)
|
||||
( git -C "/workspace/repos/$m" archive "$rev" | tar -x -C "$src" \
|
||||
&& cd "$src" && mvn -q -B -Dstyle.color=never -DskipTests -Dmaven.repo.local="$repo_local" install ) \
|
||||
|| echo "WARNING: could not install $m into $repo_local; services that depend on it won't build until it is" >&2
|
||||
rm -rf "$src"
|
||||
done
|
||||
}
|
||||
_sbl_use() { # <member> <local repo>
|
||||
local d="/workspace/repos/$1"
|
||||
[ -f "$d/pom.xml" ] || return 0
|
||||
mkdir -p "$d/.mvn" "$d/.git/info"
|
||||
printf -- '-Dmaven.repo.local=%s\n' "$2" > "$d/.mvn/maven.config"
|
||||
grep -qxF '.mvn/maven.config' "$d/.git/info/exclude" 2>/dev/null \
|
||||
|| printf '\n# raccoon-explore: selects this service'"'"'s local Maven repo\n.mvn/maven.config\n' >> "$d/.git/info/exclude"
|
||||
}
|
||||
if [ -n "$IS_POLYGLOT" ] && grep -qs 'com\.swingbell' /workspace/repos/common-repository/pom.xml; then
|
||||
_sbl_install "$HOME/.m2/repository" common-repository common-aws-service jwt-encryption-decryption reports
|
||||
_sbl_install "$HOME/.m2/bmm-repository" book-my-minutes-common-repository book-my-minutes-common-aws-service reports
|
||||
for d in /workspace/repos/book-my-minutes-* /workspace/repos/meetings; do _sbl_use "$(basename "$d")" "$HOME/.m2/bmm-repository"; done
|
||||
# These two were written against a different revision of their library than the one pinned.
|
||||
_sbl_install "$HOME/.m2/nhcx-provider-repository" common-repository@208aea7be jwt-encryption-decryption
|
||||
_sbl_use nhcx-provider "$HOME/.m2/nhcx-provider-repository"
|
||||
_sbl_install "$HOME/.m2/bmm-jobs-repository" book-my-minutes-common-repository@f15e7c128
|
||||
_sbl_use book-my-minutes-jobs "$HOME/.m2/bmm-jobs-repository"
|
||||
fi
|
||||
|
||||
# Mirror Harbor's reduced toolset in the interactive Explore session. Use
|
||||
# Harbor's /opt path when available, but fall back to a user-writable path for
|
||||
# generic devcontainer fixtures that run lifecycle hooks as a non-root user.
|
||||
@@ -528,8 +561,8 @@ bash /workspace/welcome.sh explore 2>/dev/null
|
||||
|
||||
_AK="fde503c3bdb6e5cc9c48b1f8e4c2abeb"
|
||||
_DK="e966e45af5ad1a18005f9fdb831186ea"
|
||||
_WID="w-mun3wr6n-v83f"
|
||||
_VER="1.0.0"
|
||||
_WID="w-muvydvub-qn4j"
|
||||
_VER="1.0.1"
|
||||
_CT="explore"
|
||||
_RP=$(node -e "try{process.stdout.write(require('/workspace/toolkit.json').repo)}catch{}" 2>/dev/null)
|
||||
_SID="$(date +%s)-$$"
|
||||
@@ -415,6 +415,24 @@ while (current) {
|
||||
current = byUuid[current]?.parentUuid || null;
|
||||
}
|
||||
|
||||
// Parallel tool calls can be written call, call, result, result: the first result then hangs
|
||||
// off the first call on a side branch the walk above never visits, so add those back.
|
||||
const blocksOf = (e) => (Array.isArray(e?.message?.content) ? e.message.content : []);
|
||||
const activeToolUseIds = new Set();
|
||||
for (const uuid of activeBranchUuids) {
|
||||
for (const b of blocksOf(byUuid[uuid])) if (b?.type === 'tool_use') activeToolUseIds.add(b.id);
|
||||
}
|
||||
for (const e of Object.values(byUuid)) {
|
||||
if (activeBranchUuids.has(e.uuid) || !activeBranchUuids.has(e.parentUuid)) continue;
|
||||
const blocks = blocksOf(e);
|
||||
if (
|
||||
blocks.length &&
|
||||
blocks.every((b) => b?.type === 'tool_result' && activeToolUseIds.has(b.tool_use_id))
|
||||
) {
|
||||
activeBranchUuids.add(e.uuid);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: filter — keep entries on the active branch.
|
||||
//
|
||||
// Claude Code writes several bookkeeping entry types alongside the message
|
||||
@@ -743,7 +761,7 @@ fi
|
||||
# Install conversation so the authoring harness can resume it
|
||||
${
|
||||
IS_CLAUDE
|
||||
? `ENCODED_CWD=$(echo "$PWD" | sed 's|/|-|g; s|^-||')
|
||||
? `ENCODED_CWD=$(echo "$PWD" | sed 's|[^a-zA-Z0-9]|-|g; s|^-||')
|
||||
DEST_DIR="$HOME/.claude/projects/-$ENCODED_CWD"
|
||||
mkdir -p "$DEST_DIR"
|
||||
cp "$SCRIPT_DIR/session.jsonl" "$DEST_DIR/$SESSION_UUID.jsonl"
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* Plugin-side re-export, so snapshot-to-task.ts resolves `./lib/secret-scrub`
|
||||
* both here and in the toolkit's flat scripts/ dir.
|
||||
*/
|
||||
export * from '../../../../static/scripts/lib/secret-scrub';
|
||||
@@ -162,10 +162,13 @@ function replacePrefixAtBoundary(haystack: string, needle: string, replacement:
|
||||
}
|
||||
}
|
||||
|
||||
/** Must match Claude Code's `.claude/projects/` folder names. */
|
||||
const encodeProjectDir = (p: string) => p.replace(/[^a-zA-Z0-9]/g, '-');
|
||||
|
||||
/** Replace a prefix and its dash-encoded form (`.claude/projects/<encoded>/`). */
|
||||
function stripBothForms(haystack: string, needle: string, replacement: string): string {
|
||||
const out = literalReplaceAll(haystack, needle, replacement);
|
||||
return literalReplaceAll(out, needle.replace(/\//g, '-'), replacement.replace(/\//g, '-'));
|
||||
return literalReplaceAll(out, encodeProjectDir(needle), replacement.replace(/\//g, '-'));
|
||||
}
|
||||
|
||||
export function sanitizeSessionJsonl(raw: string, opts: SanitizeOptions = {}): SanitizeResult {
|
||||
@@ -193,7 +196,7 @@ export function sanitizeSessionJsonl(raw: string, opts: SanitizeOptions = {}): S
|
||||
// EVERY root before ANY home dir: a home pass run between roots would rewrite a
|
||||
// sibling root's own prefix, leaving it unmatched when its turn came.
|
||||
for (const prefix of prefixes) {
|
||||
const encodedPrefix = prefix.replace(/\//g, '-');
|
||||
const encodedPrefix = encodeProjectDir(prefix);
|
||||
working = replacePrefixAtBoundary(working, prefix, placeholder);
|
||||
working = literalReplaceAll(working, encodedPrefix, placeholder.replace(/\//g, '-'));
|
||||
prefixStripped ??= prefix;
|
||||
@@ -206,7 +209,7 @@ export function sanitizeSessionJsonl(raw: string, opts: SanitizeOptions = {}): S
|
||||
.filter((h): h is string => h !== null && !prefixes.includes(h))
|
||||
);
|
||||
for (const homeDir of homeDirs) {
|
||||
const encodedHomeDir = homeDir.replace(/\//g, '-');
|
||||
const encodedHomeDir = encodeProjectDir(homeDir);
|
||||
working = replacePrefixAtBoundary(working, homeDir, HOME_DIR_PLACEHOLDER);
|
||||
working = literalReplaceAll(working, encodedHomeDir, HOME_DIR_PLACEHOLDER.replace(/\//g, '-'));
|
||||
homeDirStripped ??= homeDir;
|
||||
@@ -26,6 +26,7 @@ import { stripAuthoringScaffolding, truncationIndex, turnsFromLines } from './ha
|
||||
// This script must not call cpSync — it fails EACCES on a macOS docker bind mount.
|
||||
import { holisticRubricScaffoldFor } from './holistic-rubric-scaffold';
|
||||
import { copyTree } from './lib/copy-tree';
|
||||
import { readEnvSecrets, scrubTaskSecrets } from './lib/secret-scrub';
|
||||
import { collectCwds, sanitizeSessionJsonl } from './sanitize-session-jsonl';
|
||||
|
||||
// --- CLI ---
|
||||
@@ -576,6 +577,13 @@ if (existsSync(sessionDir) && statSync(sessionDir).isDirectory()) {
|
||||
mkdirSync(join(taskDir, 'environment', 'session'), { recursive: true });
|
||||
}
|
||||
|
||||
// The Explore agent can print the .env it runs with (`env`, `cat .env`), so its keys can be in
|
||||
// the capture. The scrub is silent: the worker didn't put them there and has nothing to do.
|
||||
const secretScan = scrubTaskSecrets(taskDir, readEnvSecrets(repoRoot));
|
||||
for (const file of secretScan.flagged) {
|
||||
log.warn({ file }, 'This file holds an API key from your .env. Remove it before you submit.');
|
||||
}
|
||||
|
||||
// The harness that captured the snapshot; the trial runs this one.
|
||||
const harness =
|
||||
typeof metadata.harness === 'string' && metadata.harness ? metadata.harness : 'claude-code';
|
||||
1
worker-toolkit-potion-polyglot-v1.0.1/explore/repos
Symbolic link
1
worker-toolkit-potion-polyglot-v1.0.1/explore/repos
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/eric/workspaces/dataannotation/project-2/worker-toolkit-potion-polyglot-v1.0.1/repos
|
||||
@@ -319,6 +319,18 @@ _node_bin() {
|
||||
d=$(ls -d "$nvm_dir"/versions/node/v"$major".* 2>/dev/null | sort -V | tail -1)
|
||||
[ -n "$d" ] && printf '%s/bin' "$d"
|
||||
}
|
||||
# First dev-server script <dir>/package.json defines. `next start` serves a production build that
|
||||
# a fresh checkout doesn't have, so a Next app boots `dev` instead.
|
||||
_dev_script() {
|
||||
node -e "const s=(require('$1/package.json').scripts)||{};const o=['start','dev','develop','serve'].filter(k=>s[k]&&!(k==='start'&&/^next start/.test(s[k])&&s.dev));if(o[0])process.stdout.write(o[0])" 2>/dev/null
|
||||
}
|
||||
# Path the app serves under in dev: a path "homepage" (CRA) or a Next.js basePath, e.g. /app.
|
||||
_homepage_path() {
|
||||
local p
|
||||
p=$(node -e "const h=(require('$1/package.json').homepage)||'';let p=h;try{p=new URL(h).pathname}catch{};if(/^\/[^/]/.test(p))process.stdout.write(p.replace(/\/$/,''))" 2>/dev/null)
|
||||
[ -n "$p" ] || p=$(cat "$1"/next.config.* 2>/dev/null | sed -nE "s/^[[:space:]]*basePath:[[:space:]]*['\"](\/[^'\"]+)['\"].*/\1/p" | head -1)
|
||||
printf '%s' "$p"
|
||||
}
|
||||
# Symlink ./node_modules (cwd = the dir being installed) to a container-local tree keyed by
|
||||
# <key> — see the ENFILE rationale at the call site. The target must itself be named
|
||||
# `node_modules` (Node resolves the symlink, then walks ancestors for that literal name),
|
||||
@@ -637,36 +649,37 @@ setup_repo() {
|
||||
else
|
||||
_mark_ctr_nodeps "$repo"
|
||||
printf " ${YELLOW}\xe2\x9a\xa0 %s: dependencies did not install${RESET} \xe2\x80\x94 explore-only in this container.\n ${GRAY}Reading the code, git and the editor still work; running the app or its tests will not.\n Usually a pin with no build for this machine's architecture. To retry: rm %s/%s.done${RESET}\n" "$repo" "$CTR_MARKER_DIR" "$repo"
|
||||
_mark_ctr_setup "$repo"; return 0
|
||||
fi
|
||||
_mark_ctr_setup "$repo"; return 0
|
||||
fi
|
||||
_py_have "$ver" || { printf " ${GRAY}(Python %s not in this image; skipping deps \xe2\x80\x94 explore-only)${RESET}\n" "$ver"; _mark_ctr_setup "$repo"; return 0; }
|
||||
# Some poetry repos depend on sibling repos via `git = "ssh://git@github.com/AskZeta/<name>.git"`,
|
||||
# which can't resolve in the container (no SSH key, no network). The deps are TRANSITIVE
|
||||
# (cx-chatbot → compiler-agent → agent-tools → leaves), so rewrite the target AND every
|
||||
# sibling pyproject to local path deps — else poetry shells out to `ssh` for a transitive
|
||||
# git dep and fails ("No such file or directory: 'ssh'").
|
||||
for pp in /workspace/repos/*/pyproject.toml; do
|
||||
[ -f "$pp" ] && _rewrite_askzeta_git_deps "$pp"
|
||||
done
|
||||
( cd "$dir" && export PATH="$PYENV_PATH:$PATH" PYENV_VERSION="$ver" \
|
||||
&& { [ -f .env.example ] && cp -n .env.example .env; true; } \
|
||||
&& { for kv in $bootenv; do grep -qxF "$kv" .env 2>/dev/null || echo "$kv" >> .env; done; true; } \
|
||||
&& { if [ -f pyproject.toml ]; then \
|
||||
# Every member Dockerfile sets this; without it poetry builds a .venv here
|
||||
# that the trial image has no equivalent of.
|
||||
poetry config virtualenvs.create false 2>/dev/null || true; \
|
||||
# The git→path rewrite invalidates poetry.lock ("changed significantly"), so
|
||||
# re-lock preserving pins: --no-update on poetry 1.x, the default on 2.x.
|
||||
poetry lock --no-update 2>/dev/null || poetry lock 2>/dev/null || true; \
|
||||
# --no-root: install deps only, not the project package itself. Some members'
|
||||
# pyproject package name doesn't map to a folder poetry can find ("No file/folder
|
||||
# found for package <x>"), which fails the whole install. The worker explores +
|
||||
# runs the code from the repo dir (cwd on path), so the project never needs to be
|
||||
# pip-installed as a package. Mirrors the harbor build.
|
||||
poetry install --no-interaction --no-root; \
|
||||
elif [ -f requirements.txt ]; then pip install -r requirements.txt; \
|
||||
elif [ -f setup.py ]; then pip install -e .; else true; fi; } ) || return 1 ;;
|
||||
else
|
||||
_py_have "$ver" || { printf " ${GRAY}(Python %s not in this image; skipping deps \xe2\x80\x94 explore-only)${RESET}\n" "$ver"; _mark_ctr_setup "$repo"; return 0; }
|
||||
# Some poetry repos depend on sibling repos via `git = "ssh://git@github.com/AskZeta/<name>.git"`,
|
||||
# which can't resolve in the container (no SSH key, no network). The deps are TRANSITIVE
|
||||
# (cx-chatbot → compiler-agent → agent-tools → leaves), so rewrite the target AND every
|
||||
# sibling pyproject to local path deps — else poetry shells out to `ssh` for a transitive
|
||||
# git dep and fails ("No such file or directory: 'ssh'").
|
||||
for pp in /workspace/repos/*/pyproject.toml; do
|
||||
[ -f "$pp" ] && _rewrite_askzeta_git_deps "$pp"
|
||||
done
|
||||
( cd "$dir" && export PATH="$PYENV_PATH:$PATH" PYENV_VERSION="$ver" \
|
||||
&& { [ -f .env.example ] && cp -n .env.example .env; true; } \
|
||||
&& { for kv in $bootenv; do grep -qxF "$kv" .env 2>/dev/null || echo "$kv" >> .env; done; true; } \
|
||||
&& { if [ -f pyproject.toml ]; then \
|
||||
# Every member Dockerfile sets this; without it poetry builds a .venv here
|
||||
# that the trial image has no equivalent of.
|
||||
poetry config virtualenvs.create false 2>/dev/null || true; \
|
||||
# The git→path rewrite invalidates poetry.lock ("changed significantly"), so
|
||||
# re-lock preserving pins: --no-update on poetry 1.x, the default on 2.x.
|
||||
poetry lock --no-update 2>/dev/null || poetry lock 2>/dev/null || true; \
|
||||
# --no-root: install deps only, not the project package itself. Some members'
|
||||
# pyproject package name doesn't map to a folder poetry can find ("No file/folder
|
||||
# found for package <x>"), which fails the whole install. The worker explores +
|
||||
# runs the code from the repo dir (cwd on path), so the project never needs to be
|
||||
# pip-installed as a package. Mirrors the harbor build.
|
||||
poetry install --no-interaction --no-root; \
|
||||
elif [ -f requirements.txt ]; then pip install -r requirements.txt; \
|
||||
elif [ -f setup.py ]; then pip install -e .; else true; fi; } ) || return 1
|
||||
fi ;;
|
||||
rust)
|
||||
command -v cargo >/dev/null 2>&1 || { printf " ${GRAY}(Rust not in this image; skipping build \xe2\x80\x94 explore-only)${RESET}\n"; _mark_ctr_setup "$repo"; return 0; }
|
||||
# Build to a container-local target dir (same ENFILE/bind-mount rationale as
|
||||
@@ -696,7 +709,15 @@ setup_repo() {
|
||||
printf " ${GRAY}preparing %s (one-time; details in ${RESET}${GRAY}run-app --logs${RESET}${GRAY})\xe2\x80\xa6${RESET}\n" "$repo"
|
||||
if ( cd "$dir" \
|
||||
&& export PATH="${spath:+$spath:}$PATH" \
|
||||
&& case "$kind" in ruby) export RBENV_VERSION="$ver" ;; python) export PYENV_VERSION="$ver" ;; esac \
|
||||
&& case "$kind" in
|
||||
ruby) export RBENV_VERSION="$ver" ;;
|
||||
python)
|
||||
if _py_uv_ok "$ver"; then
|
||||
. "$(_uv_venv_dir "$repo")/bin/activate"
|
||||
else
|
||||
export PYENV_VERSION="$ver"
|
||||
fi ;;
|
||||
esac \
|
||||
&& { for kv in $bootenv; do export "$kv"; done; } \
|
||||
&& eval "$setupcmd" ) > "$slog" 2>&1; then
|
||||
printf " ${GRAY}\xe2\x9c\x93 %s prepared${RESET}\n" "$repo"
|
||||
@@ -793,10 +814,8 @@ start_poly() {
|
||||
if [ -n "$startcmd" ]; then
|
||||
cmd="env $bootenv PORT=3000 BROWSER=none HOST=0.0.0.0 $startcmd"
|
||||
else
|
||||
local s2=""
|
||||
for s2 in start dev develop serve; do
|
||||
if node -e "process.exit((((require('$dir/package.json')||{}).scripts)||{})['$s2']?0:1)" 2>/dev/null; then break; else s2=""; fi
|
||||
done
|
||||
local s2
|
||||
s2=$(_dev_script "$dir")
|
||||
if [ -z "$s2" ]; then
|
||||
printf " ${GRAY}%s: deps installed, no dev-server script \xe2\x80\x94 run its tests directly (${RESET}${GRAY}yarn test${RESET}${GRAY}).${RESET}\n" "$repo"
|
||||
return 0
|
||||
@@ -811,10 +830,7 @@ start_poly() {
|
||||
return 0
|
||||
else
|
||||
# CRA / generic: first dev-server script the repo defines, bound to :3000.
|
||||
local s
|
||||
for s in start dev develop serve; do
|
||||
if node -e "process.exit((((require('$dir/package.json')||{}).scripts)||{})['$s']?0:1)" 2>/dev/null; then sc="$s"; break; fi
|
||||
done
|
||||
sc=$(_dev_script "$dir")
|
||||
if [ -z "$sc" ]; then
|
||||
printf " ${GRAY}%s: deps installed, no dev-server script \xe2\x80\x94 run its tests directly (${RESET}${GRAY}yarn test${RESET}${GRAY}).${RESET}\n" "$repo"
|
||||
return 0
|
||||
@@ -840,7 +856,11 @@ start_poly() {
|
||||
craenv="CI=true DANGEROUSLY_DISABLE_HOST_CHECK=true"
|
||||
case "${ver%%.*}" in 1[7-9]|[2-9][0-9]) craenv="NODE_OPTIONS=--openssl-legacy-provider $craenv" ;; esac
|
||||
fi
|
||||
cmd="env $bootenv $craenv PATH=$nbin:\$PATH PORT=3000 BROWSER=none HOST=0.0.0.0 yarn $sc"
|
||||
# Vite ignores PORT/HOST and listens on localhost:5173, which the published port can't reach.
|
||||
local viteargs=""
|
||||
node -e "process.exit(/^vite( |$)(?!build|preview)/.test((require('$dir/package.json').scripts||{})['$sc']||'')?0:1)" 2>/dev/null \
|
||||
&& viteargs=" --host 0.0.0.0 --port 3000"
|
||||
cmd="env $bootenv $craenv PATH=$nbin:\$PATH PORT=3000 BROWSER=none HOST=0.0.0.0 yarn $sc$viteargs"
|
||||
fi ;;
|
||||
python)
|
||||
if _ctr_nodeps "$repo"; then
|
||||
@@ -890,9 +910,9 @@ start_poly() {
|
||||
|| printf " ${YELLOW}\xe2\x9a\xa0 strongsuit_phx database prep failed \xe2\x80\x94 ${RESET}${GRAY}run-app --logs${RESET}\n"
|
||||
if [ -d "$phx_dir/deps" ] && [ -d "$phx_dir/_build" ]; then
|
||||
printf " ${CYAN}\xe2\x96\xb6${RESET} starting strongsuit_phx (elixir)\xe2\x80\xa6\n"
|
||||
# Derived, not a second env var: the app sends PHX_AUTH_TOKEN and the phx plug
|
||||
# compares against API_AUTH_TOKEN, and a drift shows up only as silent 401s.
|
||||
_spawn phx "$phx_dir" "env MIX_ENV=dev PHX_PORT=$COMPANION_PORT API_AUTH_TOKEN=${PHX_AUTH_TOKEN:-dummy} mix phx.server"
|
||||
# The app sends PHX_AUTH_TOKEN and the phx plug compares API_AUTH_TOKEN; both default
|
||||
# to the same value, so pass one only when the other was set.
|
||||
_spawn phx "$phx_dir" "env MIX_ENV=dev PHX_PORT=$COMPANION_PORT ${PHX_AUTH_TOKEN:+API_AUTH_TOKEN=$PHX_AUTH_TOKEN} mix phx.server"
|
||||
_wait_tcp "$COMPANION_PORT" 45 || printf " ${YELLOW}\xe2\x9a\xa0 strongsuit_phx didn't come up \xe2\x80\x94 ${RESET}${GRAY}run-app --logs${RESET}\n"
|
||||
else
|
||||
printf " ${GRAY}strongsuit_phx isn't built \xe2\x80\x94 the backend on :%s will be absent.\n" "$COMPANION_PORT"
|
||||
@@ -908,6 +928,8 @@ start_poly() {
|
||||
local landing=""
|
||||
case "$repo" in
|
||||
strongsuit-app) landing="/dev-login" ;;
|
||||
ABDM-FE) landing="/app/login" ;;
|
||||
*) [ "$kind" = node ] && landing=$(_homepage_path "$dir") ;;
|
||||
esac
|
||||
printf " ${CYAN}\xe2\x9c\x85 %s is up${RESET} open ${CYAN}http://localhost:%s%s${RESET}\n" "$repo" "$CLIENT_HOST_PORT" "$landing"
|
||||
# Per-member "how do I actually get in" notes. Only members whose landing page needs
|
||||
@@ -922,11 +944,15 @@ start_poly() {
|
||||
printf " Auth0 and cannot work offline.${RESET}\n"
|
||||
;;
|
||||
ABDM-FE)
|
||||
printf " ${GRAY}This app is served under a ${RESET}${GRAY}/app${RESET}${GRAY} basename, so the bare URL above renders\n"
|
||||
printf " nothing. Open ${RESET}${CYAN}http://localhost:%s/app/login${RESET}${GRAY} instead.\n" "$CLIENT_HOST_PORT"
|
||||
printf " Sign-in itself calls hosted services that aren't reachable offline, so the\n"
|
||||
printf " ${GRAY}Sign-in calls hosted services that aren't reachable offline, so the\n"
|
||||
printf " login page is as far as you can get — read and edit the code from there.${RESET}\n"
|
||||
;;
|
||||
book-my-minutes-app)
|
||||
printf " ${GRAY}This is the signed-in dashboard: every page needs a session, and signing in happens\n"
|
||||
printf " on a separate hosted app that isn't reachable offline, so the page above stays\n"
|
||||
printf " blank. Read and edit the code instead, or run ${RESET}${GRAY}book-my-minutes-onboarding${RESET}${GRAY} to see the\n"
|
||||
printf " public side of the product.${RESET}\n"
|
||||
;;
|
||||
search-api-v2)
|
||||
printf " ${GRAY}Browse and try the API at ${RESET}${CYAN}http://localhost:%s/docs${RESET}${GRAY}.\n" "$CLIENT_HOST_PORT"
|
||||
printf " Sign-in goes through a hosted identity provider that isn't reachable offline,\n"
|
||||
Binary file not shown.
@@ -271,9 +271,9 @@
|
||||
}
|
||||
],
|
||||
"defaultRepo": "potion-app",
|
||||
"version": "1.0.0",
|
||||
"buildSha": "f62b06f",
|
||||
"packedFrom": "565c9589c46926ee3025f82d28f31be0aae007e8",
|
||||
"version": "1.0.1",
|
||||
"buildSha": "5e48703",
|
||||
"packedFrom": "9b0c19e8f35875c162ad4e6129dfbdcbfc5f177c",
|
||||
"blockedHosts": [
|
||||
"sendpotion.com",
|
||||
"www.sendpotion.com",
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 1,
|
||||
"stampedAt": "2026-09-29T20:06:24.427Z",
|
||||
"stampedAt": "2026-10-06T00:41:40.440Z",
|
||||
"files": {
|
||||
"tests/test.sh": "67d84a6d694718a777dfb9d79cb629206f769b92c67d66e19d9bb98bda168d04",
|
||||
"tests/test.sh": "32eb923e2772d1a7a0393f333adeff5eb33f68132cb5cadf4480ce1eb0ad5329",
|
||||
"tests/grader-system-prompt-consolidated.md": "032ce032728a8c0b2717478b929dbd7535e07c96ffe2e991097dd2c233543275"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user