fix: misc write issues with fs + openrouter refresh (#3125)
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
Notify HeyPuter / notify (push) Has been cancelled
release-please / release-please (push) Has been cancelled

* feat: refresh openrouter models every 15 min

* fix: if no extensions, skip build step

* fix: misc write issues with fs
This commit is contained in:
Daniel Salazar
2026-05-17 12:34:08 -07:00
committed by GitHub
parent 9e7f38f473
commit 6493d66901
6 changed files with 55 additions and 26 deletions
-23
View File
@@ -6270,29 +6270,6 @@
"node": "^20.19.0 || >=22.12.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz",
"integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.1",
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
"integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
"dev": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@rolldown/binding-win32-arm64-msvc": {
"version": "1.0.0-rc.18",
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz",
@@ -1054,6 +1054,18 @@ export class LegacyFSController extends PuterController {
const operation =
typeof query.operation === 'string' ? query.operation : 'write';
// A valid write signature authorises overwriting the file's bytes,
// not structural changes. Restrict copy/move/mkdir/rename/delete/trash
// to a caller authenticated as the owner — otherwise a recipient of a
// write-share could relocate or destroy the source via this endpoint.
if (operation !== 'write' && req.actor?.user?.id !== userId) {
throw new HttpError(
403,
`'${operation}' via signed URL requires owner authentication`,
{ legacyCode: 'forbidden' },
);
}
// `write` — multipart upload, streamed directly to the v2 write path.
if (operation === 'write') {
const body = asRecord(req.body);
@@ -1087,8 +1099,19 @@ export class LegacyFSController extends PuterController {
}
// Non-write operations: route to existing service methods and sign the result.
// The signature alone authorises only byte writes to `targetEntry`. Structural
// ops (mkdir/rename/copy/move/delete) require a caller actor with explicit
// ACL on the affected paths — mirroring the unsigned counterparts above.
const record = asRecord(req.body);
const callerActor = this.#requireActor(req);
if (operation === 'mkdir') {
await assertAccess(
this.services.acl,
this.services.fs,
callerActor,
targetEntry.path,
'write',
);
const folderName =
typeof record.name === 'string'
? record.name
@@ -1110,12 +1133,26 @@ export class LegacyFSController extends PuterController {
throw new HttpError(400, '`new_name` required', {
legacyCode: 'bad_request',
});
await assertAccess(
this.services.acl,
this.services.fs,
callerActor,
targetEntry.path,
'write',
);
const renamed = await this.services.fs.rename(targetEntry, newName);
await this.#emitGuiEvent('outer.gui.item.updated', renamed);
res.json({ ...signEntry(renamed, signingCfg), path: renamed.path });
return;
}
if (operation === 'delete' || operation === 'trash') {
await assertAccess(
this.services.acl,
this.services.fs,
callerActor,
targetEntry.path,
'write',
);
// Treat trash == delete (recursive). Most clients just call delete
// directly; if a trash folder becomes important we can revisit.
await this.services.fs.remove(userId, {
@@ -1139,6 +1176,20 @@ export class LegacyFSController extends PuterController {
this.stores.fsEntry,
destRef,
);
await assertAccess(
this.services.acl,
this.services.fs,
callerActor,
targetEntry.path,
operation === 'copy' ? 'read' : 'write',
);
await assertAccess(
this.services.acl,
this.services.fs,
callerActor,
destinationParent.path,
'write',
);
const method = operation === 'copy' ? 'copy' : 'move';
const result = await this.services.fs[method](userId, {
source: targetEntry,
@@ -230,7 +230,7 @@ export class OpenRouterProvider implements IChatProvider {
});
models = resp.data.data;
kv.set('openrouterChat:models', models);
kv.set('openrouterChat:models', models, { EX: 15 * 60 }); // cache for 15 minutes
} catch (e) {
console.log(e);
}
@@ -96,7 +96,7 @@ export class TogetherAIProvider implements IChatProvider {
},
max_tokens: 1000,
});
kv.set(this.#kvKey, models, { EX: 5 * 60 });
kv.set(this.#kvKey, models, { EX: 15 * 60 });
return models;
}
+1
View File
@@ -125,4 +125,5 @@ export interface IChatProvider {
list(): string[] | Promise<string[]>;
getDefaultModel(): string;
complete(arg: ICompleteArguments): Promise<IChatCompleteResult>;
checkModeration(text: string): { flagged: boolean; categories: string[] };
}
+1 -1
View File
@@ -34,7 +34,7 @@ module.exports = async (options = {}) => {
const entries = [];
for ( const extensionsDir of extension_directories ) {
// console.log(`Reading extensions from ${extensionsDir}`);
if ( ! fs.existsSync(extensionsDir) ) continue;
// Read and process extension entries from the extensions directory
const readdir_entries = fs.readdirSync(extensionsDir, { withFileTypes: true });
for ( const entry of readdir_entries ) {