@portel/photon 1.34.0 → 1.34.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -6
- package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.js +92 -15
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +16 -7
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-marketplace.js +4 -4
- package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +21 -2
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +56 -2
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/beam.bundle.js +47 -0
- package/dist/beam.bundle.js.map +2 -2
- package/dist/daemon/server.js +57 -13
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/session-manager.d.ts +2 -1
- package/dist/daemon/session-manager.d.ts.map +1 -1
- package/dist/daemon/session-manager.js +33 -2
- package/dist/daemon/session-manager.js.map +1 -1
- package/dist/serv/auth/http-adapter.d.ts.map +1 -1
- package/dist/serv/auth/http-adapter.js +7 -2
- package/dist/serv/auth/http-adapter.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -2
- package/dist/server.js.map +1 -1
- package/package.json +4 -3
package/dist/daemon/server.js
CHANGED
|
@@ -27,7 +27,7 @@ import { getDefaultContext } from '../context.js';
|
|
|
27
27
|
import { EnvStore, resolvePhotonNamespace } from '../context-store.js';
|
|
28
28
|
import { createLogger } from '../shared/logger.js';
|
|
29
29
|
import { getErrorMessage } from '../shared/error-handler.js';
|
|
30
|
-
import { timingSafeEqual, readBody, SimpleRateLimiter, ipInAllowlist, parseAllowlistEnv, } from '../shared/security.js';
|
|
30
|
+
import { timingSafeEqual, readBody, SimpleRateLimiter, ipInAllowlist, parseAllowlistEnv, getCorsOrigin, } from '../shared/security.js';
|
|
31
31
|
import { audit } from '../shared/audit.js';
|
|
32
32
|
import { recordExecution, previewResult, readExecutionHistory, sweepAllBases as sweepExecutionHistoryBases, } from './execution-history.js';
|
|
33
33
|
import { WorkerManager } from './worker-manager.js';
|
|
@@ -1734,7 +1734,9 @@ function watchBaseForProactiveMetadata(basePath, _isDefaultBase) {
|
|
|
1734
1734
|
watchDebounce.set(debounceKey, timer);
|
|
1735
1735
|
};
|
|
1736
1736
|
try {
|
|
1737
|
-
const watcher = safeWatchDir(basePath, (_eventType, filename) => onChange(filename)
|
|
1737
|
+
const watcher = safeWatchDir(basePath, (_eventType, filename) => onChange(filename), {
|
|
1738
|
+
bunPollMaxEntries: BUN_DIR_POLL_MAX_ENTRIES,
|
|
1739
|
+
});
|
|
1738
1740
|
baseDirWatchers.set(basePath, watcher);
|
|
1739
1741
|
}
|
|
1740
1742
|
catch (err) {
|
|
@@ -1976,7 +1978,9 @@ function startWebhookServer(port) {
|
|
|
1976
1978
|
return;
|
|
1977
1979
|
webhookServer = http.createServer((req, res) => {
|
|
1978
1980
|
void (async () => {
|
|
1979
|
-
|
|
1981
|
+
const corsOrigin = getCorsOrigin(req);
|
|
1982
|
+
if (corsOrigin)
|
|
1983
|
+
res.setHeader('Access-Control-Allow-Origin', corsOrigin);
|
|
1980
1984
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
1981
1985
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, X-Webhook-Secret, X-Photon-Name');
|
|
1982
1986
|
if (req.method === 'OPTIONS') {
|
|
@@ -2968,7 +2972,7 @@ async function handleRequest(request, socket) {
|
|
|
2968
2972
|
const key = compositeKey(photonName, request.workingDir);
|
|
2969
2973
|
const sessionManager = sessionManagers.get(key);
|
|
2970
2974
|
if (sessionManager) {
|
|
2971
|
-
await sessionManager.clearInstances();
|
|
2975
|
+
await sessionManager.clearInstances('clear-instances');
|
|
2972
2976
|
}
|
|
2973
2977
|
return { type: 'result', id: request.id, success: true, data: { cleared: !!sessionManager } };
|
|
2974
2978
|
}
|
|
@@ -4638,6 +4642,7 @@ async function applyPatchToInstance(instance, photonName, ops, workingDir) {
|
|
|
4638
4642
|
*/
|
|
4639
4643
|
const IS_BUN = !!process.versions.bun;
|
|
4640
4644
|
const POLL_INTERVAL_MS = 2000; // 2s — good balance between latency and CPU
|
|
4645
|
+
const BUN_DIR_POLL_MAX_ENTRIES = parseNonNegativeEnvInt('PHOTON_BUN_DIR_POLL_MAX_ENTRIES', 512);
|
|
4641
4646
|
/** Track active poll timers so they can be cleaned up on shutdown */
|
|
4642
4647
|
const pollTimers = new Set();
|
|
4643
4648
|
function safeWatchFile(filePath, callback) {
|
|
@@ -4686,12 +4691,37 @@ function safeWatchFile(filePath, callback) {
|
|
|
4686
4691
|
},
|
|
4687
4692
|
};
|
|
4688
4693
|
}
|
|
4689
|
-
function
|
|
4694
|
+
function countDirEntries(dirPath) {
|
|
4695
|
+
try {
|
|
4696
|
+
return fs.readdirSync(dirPath).length;
|
|
4697
|
+
}
|
|
4698
|
+
catch {
|
|
4699
|
+
return null;
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
function shouldSkipBunDirPoll(dirPath, maxEntries) {
|
|
4703
|
+
if (!IS_BUN || maxEntries === 0)
|
|
4704
|
+
return false;
|
|
4705
|
+
const count = countDirEntries(dirPath);
|
|
4706
|
+
if (count === null || count <= maxEntries)
|
|
4707
|
+
return false;
|
|
4708
|
+
logger.warn('Skipping Bun directory polling watcher for oversized directory', {
|
|
4709
|
+
dir: dirPath,
|
|
4710
|
+
entries: count,
|
|
4711
|
+
maxEntries,
|
|
4712
|
+
});
|
|
4713
|
+
return true;
|
|
4714
|
+
}
|
|
4715
|
+
function safeWatchDir(dirPath, callback, options = {}) {
|
|
4690
4716
|
if (!IS_BUN) {
|
|
4691
4717
|
const w = fs.watch(dirPath, (eventType, filename) => callback(eventType, filename));
|
|
4692
4718
|
return w;
|
|
4693
4719
|
}
|
|
4694
4720
|
// Bun fallback: readdir snapshot diffing
|
|
4721
|
+
if (options.bunPollMaxEntries !== undefined &&
|
|
4722
|
+
shouldSkipBunDirPoll(dirPath, options.bunPollMaxEntries)) {
|
|
4723
|
+
return { close() { } };
|
|
4724
|
+
}
|
|
4695
4725
|
let prevEntries = new Map(); // name -> mtimeMs
|
|
4696
4726
|
try {
|
|
4697
4727
|
for (const entry of fs.readdirSync(dirPath)) {
|
|
@@ -4836,7 +4866,7 @@ function watchPhotonFile(photonName, photonPath) {
|
|
|
4836
4866
|
for (const key of keysToDelete) {
|
|
4837
4867
|
const manager = sessionManagers.get(key);
|
|
4838
4868
|
if (manager)
|
|
4839
|
-
await manager.clearInstances();
|
|
4869
|
+
await manager.clearInstances('photon-deleted');
|
|
4840
4870
|
sessionManagers.delete(key);
|
|
4841
4871
|
photonPaths.delete(key);
|
|
4842
4872
|
workingDirs.delete(key);
|
|
@@ -4914,6 +4944,22 @@ function unwatchPhotonFile(watchPath) {
|
|
|
4914
4944
|
watchDebounce.delete(watchPath);
|
|
4915
4945
|
}
|
|
4916
4946
|
}
|
|
4947
|
+
function unwatchPhotonPath(photonPath) {
|
|
4948
|
+
let watchPath = photonPath;
|
|
4949
|
+
try {
|
|
4950
|
+
watchPath = fs.realpathSync(photonPath);
|
|
4951
|
+
}
|
|
4952
|
+
catch {
|
|
4953
|
+
// Symlink target may already be gone; fall back to caller path.
|
|
4954
|
+
}
|
|
4955
|
+
unwatchPhotonFile(watchPath);
|
|
4956
|
+
const debounceKey = path.resolve(photonPath);
|
|
4957
|
+
const timer = watchDebounce.get(debounceKey);
|
|
4958
|
+
if (timer) {
|
|
4959
|
+
clearTimeout(timer);
|
|
4960
|
+
watchDebounce.delete(debounceKey);
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4917
4963
|
/**
|
|
4918
4964
|
* Detect whether a missing workingDir was renamed (moved) or deleted.
|
|
4919
4965
|
*
|
|
@@ -5041,7 +5087,7 @@ function watchWorkingDir(workingDir) {
|
|
|
5041
5087
|
for (const key of deletedDirKeys) {
|
|
5042
5088
|
const manager = sessionManagers.get(key);
|
|
5043
5089
|
if (manager)
|
|
5044
|
-
await manager.clearInstances();
|
|
5090
|
+
await manager.clearInstances('working-dir-deleted');
|
|
5045
5091
|
sessionManagers.delete(key);
|
|
5046
5092
|
photonPaths.delete(key);
|
|
5047
5093
|
workingDirs.delete(key);
|
|
@@ -5108,7 +5154,7 @@ function watchStateDir(workingDir) {
|
|
|
5108
5154
|
const key = compositeKey(filename, workingDir);
|
|
5109
5155
|
const manager = sessionManagers.get(key);
|
|
5110
5156
|
if (manager) {
|
|
5111
|
-
await manager.clearInstances();
|
|
5157
|
+
await manager.clearInstances('state-dir-deleted');
|
|
5112
5158
|
}
|
|
5113
5159
|
})();
|
|
5114
5160
|
}, 150);
|
|
@@ -5602,13 +5648,11 @@ function startupWatchPhotonDir(photonDir, defaultBase) {
|
|
|
5602
5648
|
if (photonPaths.has(photonKey) && !fs.existsSync(filePath)) {
|
|
5603
5649
|
photonPaths.delete(photonKey);
|
|
5604
5650
|
// fileWatchers is keyed by file path, not composite key
|
|
5605
|
-
|
|
5606
|
-
if (watcher) {
|
|
5607
|
-
watcher.close();
|
|
5608
|
-
fileWatchers.delete(filePath);
|
|
5609
|
-
}
|
|
5651
|
+
unwatchPhotonPath(filePath);
|
|
5610
5652
|
logger.info('Photon file removed', { photonName, path: filePath });
|
|
5611
5653
|
}
|
|
5654
|
+
}, {
|
|
5655
|
+
bunPollMaxEntries: BUN_DIR_POLL_MAX_ENTRIES,
|
|
5612
5656
|
});
|
|
5613
5657
|
if (typeof dirWatcher.on === 'function') {
|
|
5614
5658
|
dirWatcher.on('error', () => { }); // Non-fatal
|