@rubytech/create-maxy-code 0.1.486 → 0.1.487
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/payload/platform/plugins/cloudflare/PLUGIN.md +3 -3
- package/payload/platform/plugins/cloudflare/bin/portal-index-push.mjs +30 -8
- package/payload/platform/plugins/cloudflare/bin/schema-exposed-dirs.mjs +22 -2
- package/payload/platform/plugins/cloudflare/mcp/__tests__/portal-index-push.test.ts +23 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/schema-exposed-dirs.test.ts +43 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +12 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.js +12 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.d.ts +8 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.js +12 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/free-busy-url.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js +12 -2
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/publish-availability.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +17 -1
- package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
- package/payload/server/server.js +147 -102
package/package.json
CHANGED
|
@@ -71,7 +71,7 @@ The setup-done claim only fires when `curl -I https://<hostname>` issued from ou
|
|
|
71
71
|
|
|
72
72
|
## Data-portal folder index
|
|
73
73
|
|
|
74
|
-
The portal shows a sub-account's deliverable folders alongside what the client uploaded. Which folders those are is never a list in the code: it is derived per account from that account's own `SCHEMA.md` — the buckets in its `<!-- ontology-buckets -->` region plus `output`, intersected with its `allowed-top-level` block. A brand on a different vertical exposes different folders with no code change. `bin/schema-exposed-dirs.mjs` is the only place that decision is made.
|
|
74
|
+
The portal shows a sub-account's deliverable folders alongside what the client uploaded. Which folders those are is never a list in the code: it is derived per account from that account's own `SCHEMA.md` — the buckets in its `<!-- ontology-buckets -->` region plus `output`, intersected with its `allowed-top-level` block. A brand on a different vertical exposes different folders with no code change. `bin/schema-exposed-dirs.mjs` is the only place that decision is made. An operator can add non-ontology folders (a workflow folder such as `inbound-invoices`) by listing them in `exposeFolders` in the account's `data-portal.json`; each must match `[A-Za-z0-9_-]+` and is never a system folder (`agents`, `secrets`, `specialists`, `state`, `memory`, `cache`, `logs`, `tmp`) or dot-prefixed. Those extras union on top of the schema-derived set and, unlike ontology buckets, are not intersected with `allowed-top-level`; empty or absent behaves as before.
|
|
75
75
|
|
|
76
76
|
`bin/portal-index-push.mjs` walks those folders and full-replaces the account's rows in the portal's D1 `directory` table each cycle. Metadata only — names, sizes, times — so listing survives the device being offline, and a deleted or renamed file converges on the next cycle without a tombstone. The scheduling heartbeat runs it hourly for every account carrying a `data-portal.json`; it is also runnable standalone, which is how it stays operable while the heartbeat spawn path is unproven on a device.
|
|
77
77
|
|
|
@@ -82,7 +82,7 @@ Exposure is narrowed twice. The install re-derives the account's exposed set fro
|
|
|
82
82
|
What to watch, on the device:
|
|
83
83
|
|
|
84
84
|
```
|
|
85
|
-
[portal-index] op=resolve account=<id> schemaPresent=<bool> allowed=<n> domainBuckets=<dirs|none> exposed=<dirs|none>
|
|
85
|
+
[portal-index] op=resolve account=<id> schemaPresent=<bool> allowed=<n> domainBuckets=<dirs|none> extraFolders=<n> exposed=<dirs|none>
|
|
86
86
|
[portal-index] op=replace account=<id> deleted=all inserted=<n>
|
|
87
87
|
[portal-index] op=verify account=<id> rowsAfter=<n> expected=<n>
|
|
88
88
|
[portal-index] op=collision account=<id> dir=<dir> claimedBy=plugin-owned
|
|
@@ -90,7 +90,7 @@ What to watch, on the device:
|
|
|
90
90
|
[portal-index] op=audit accounts=<n> stale=<n> never-ran=<n>
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
`exposed=none` where deliverables exist means the schema was missing or carried no ontology region — not that the walk found nothing. `rowsAfter` disagreeing with `expected` is a half-applied replace. An `op=collision` line explains a folder that has silently vanished from the client's view, which nothing else would report. `op=exposed-changed` fires only on a delta, so it is rare and always worth reading; a `removed=` naming a populated bucket is the client-visible regression.
|
|
93
|
+
`exposed=none` where deliverables exist means the schema was missing or carried no ontology region — not that the walk found nothing. `rowsAfter` disagreeing with `expected` is a half-applied replace. An `op=collision` line explains a folder that has silently vanished from the client's view, which nothing else would report. `op=exposed-changed` fires only on a delta, so it is rare and always worth reading; a `removed=` naming a populated bucket is the client-visible regression. `extraFolders=<n>` counts the folders the operator requested in `exposeFolders`; a non-zero `extraFolders` with none of those names in `exposed=` means the guard dropped every one.
|
|
94
94
|
|
|
95
95
|
`op=audit` is the standing check, and it is the only one that speaks when the push does **not**. It runs in-process inside the heartbeat rather than as a spawn, and reads only local state, so it cannot be blinded by the same failure it is watching for. It fires hourly even when everything is healthy — an audit that goes quiet on success is indistinguishable from an audit that stopped running. `result=never-ran` on an account with a `data-portal.json` means no cycle has ever succeeded for it; `result=stale` with `ageMin` means cycles have stopped. Its input is written only after `op=verify`, so a stale entry means the push stopped succeeding rather than merely stopped logging.
|
|
96
96
|
|
|
@@ -42,6 +42,20 @@ function readEnvFile(path) {
|
|
|
42
42
|
return out
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/** Operator-named extra folders for an account, from its data-portal.json.
|
|
46
|
+
* Absent file, absent key, or a non-array value all yield []. The resolver
|
|
47
|
+
* applies the charset-and-system guard; this reader does not pre-filter, so the
|
|
48
|
+
* `extraFolders=<n>` count reflects what the operator asked for.
|
|
49
|
+
* @param {string} accountDir @returns {Promise<string[]>} */
|
|
50
|
+
export async function readExposeFolders(accountDir) {
|
|
51
|
+
try {
|
|
52
|
+
const cfg = JSON.parse(await readFile(join(accountDir, 'data-portal.json'), 'utf8'))
|
|
53
|
+
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === 'string') : []
|
|
54
|
+
} catch {
|
|
55
|
+
return []
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
/**
|
|
46
60
|
* Files under `exposed` dirs, at any depth. Dot-prefixed entries are skipped at
|
|
47
61
|
* every level: `.uploads-tmp/` holds in-flight upload partials (files.ts:25) and
|
|
@@ -85,7 +99,7 @@ export async function walkExposed(accountDir, exposed) {
|
|
|
85
99
|
* @param {{ accountDir: string, accountId: string, dbName: string,
|
|
86
100
|
* client: { query: (sql: string, params?: unknown[]) => Promise<Record<string, unknown>[]> },
|
|
87
101
|
* log: (line: string) => void, nowIso: string,
|
|
88
|
-
* previousExposed?: string[] }} opts
|
|
102
|
+
* exposeFolders?: string[], previousExposed?: string[] }} opts
|
|
89
103
|
* @returns {Promise<{ exposed: string[], rows: number, schemaPresent: boolean }>}
|
|
90
104
|
*/
|
|
91
105
|
export async function pushAccount(opts) {
|
|
@@ -98,10 +112,15 @@ export async function pushAccount(opts) {
|
|
|
98
112
|
schemaText = null
|
|
99
113
|
}
|
|
100
114
|
|
|
101
|
-
const
|
|
115
|
+
const exposeFolders = Array.isArray(opts.exposeFolders) ? opts.exposeFolders : []
|
|
116
|
+
const r = resolveExposedDirs(schemaText, exposeFolders)
|
|
117
|
+
// extraFolders is the REQUESTED count, not the accepted count: a non-zero
|
|
118
|
+
// extraFolders with none of those names in exposed= means the guard dropped
|
|
119
|
+
// every one, which is distinct from none configured (extraFolders=0).
|
|
102
120
|
log(
|
|
103
121
|
`${TAG} op=resolve account=${accountId} schemaPresent=${r.schemaPresent} allowed=${r.allowed.length} ` +
|
|
104
|
-
`domainBuckets=${r.domainBuckets.join(',') || 'none'}
|
|
122
|
+
`domainBuckets=${r.domainBuckets.join(',') || 'none'} extraFolders=${exposeFolders.length} ` +
|
|
123
|
+
`exposed=${r.exposed.join(',') || 'none'}`,
|
|
105
124
|
)
|
|
106
125
|
// The generator gives a plugin-claimed dir to the plugin-owned region only
|
|
107
126
|
// (account-schema-owned-dirs.py:287), so this is normally silent. When it is
|
|
@@ -212,10 +231,10 @@ function arg(flag) {
|
|
|
212
231
|
* Every account carrying a data-portal.json has a portal. The file's presence is
|
|
213
232
|
* the enrolment signal — there is no separate flag to keep in sync with it.
|
|
214
233
|
* @param {string} accountsRoot
|
|
215
|
-
* @returns {Promise<{accountDir: string, accountId: string, dbName: string}[]>}
|
|
234
|
+
* @returns {Promise<{accountDir: string, accountId: string, dbName: string, exposeFolders: string[]}[]>}
|
|
216
235
|
*/
|
|
217
236
|
async function portalAccounts(accountsRoot) {
|
|
218
|
-
/** @type {{accountDir: string, accountId: string, dbName: string}[]} */ const out = []
|
|
237
|
+
/** @type {{accountDir: string, accountId: string, dbName: string, exposeFolders: string[]}[]} */ const out = []
|
|
219
238
|
/** @type {string[]} */ let names
|
|
220
239
|
try {
|
|
221
240
|
names = await readdir(accountsRoot)
|
|
@@ -227,7 +246,10 @@ async function portalAccounts(accountsRoot) {
|
|
|
227
246
|
try {
|
|
228
247
|
const cfg = JSON.parse(await readFile(join(accountDir, 'data-portal.json'), 'utf8'))
|
|
229
248
|
if (typeof cfg?.portalDbName === 'string' && cfg.portalDbName) {
|
|
230
|
-
|
|
249
|
+
const exposeFolders = Array.isArray(cfg?.exposeFolders)
|
|
250
|
+
? cfg.exposeFolders.filter((f) => typeof f === 'string')
|
|
251
|
+
: []
|
|
252
|
+
out.push({ accountDir, accountId, dbName: cfg.portalDbName, exposeFolders })
|
|
231
253
|
} else {
|
|
232
254
|
console.error(`${TAG} op=skip account=${accountId} reason=no-portalDbName`)
|
|
233
255
|
}
|
|
@@ -292,7 +314,7 @@ async function main() {
|
|
|
292
314
|
process.exit(1)
|
|
293
315
|
}
|
|
294
316
|
|
|
295
|
-
/** @type {{accountDir: string, accountId: string, dbName: string}[]} */ let targets
|
|
317
|
+
/** @type {{accountDir: string, accountId: string, dbName: string, exposeFolders: string[]}[]} */ let targets
|
|
296
318
|
if (process.argv.includes('--all-accounts')) {
|
|
297
319
|
// Same derivation as the platform's own DATA_ROOT (data-path.ts:19), from
|
|
298
320
|
// the PLATFORM_ROOT the heartbeat already passes.
|
|
@@ -307,7 +329,7 @@ async function main() {
|
|
|
307
329
|
console.error(`${TAG} op=usage --account-dir <dir> --account <id> --db <name>`)
|
|
308
330
|
process.exit(1)
|
|
309
331
|
}
|
|
310
|
-
targets = [{ accountDir, accountId, dbName }]
|
|
332
|
+
targets = [{ accountDir, accountId, dbName, exposeFolders: await readExposeFolders(accountDir) }]
|
|
311
333
|
}
|
|
312
334
|
|
|
313
335
|
const state = readState(platformRoot)
|
|
@@ -21,6 +21,18 @@ const OWNED_END = '<!-- plugin-owned-dirs:end -->'
|
|
|
21
21
|
/** The delivery convention, always exposed when the account allows it. */
|
|
22
22
|
export const ALWAYS_EXPOSED = 'output'
|
|
23
23
|
|
|
24
|
+
/** Folders that must never be named into exposure, even by an operator. The
|
|
25
|
+
* dot-prefixed `.git`/`.claude` are already excluded by the charset guard
|
|
26
|
+
* below. This is the not-system half of the extra-folder bound. */
|
|
27
|
+
const SYSTEM_DIRS = new Set(['agents', 'secrets', 'specialists', 'state', 'memory', 'cache', 'logs', 'tmp'])
|
|
28
|
+
|
|
29
|
+
/** An operator-named extra folder is accepted only as a single safe segment
|
|
30
|
+
* (the same charset the per-person `--folders` grant enforces) and never a
|
|
31
|
+
* system dir. This drops dot-prefixed names, slashes, and traversal such as
|
|
32
|
+
* `jobs/../secrets`, which would otherwise let the push walk index files under
|
|
33
|
+
* a system dir that the fetch route would refuse to serve. */
|
|
34
|
+
const SAFE_FOLDER = /^[A-Za-z0-9_-]+$/
|
|
35
|
+
|
|
24
36
|
/**
|
|
25
37
|
* The generator writes the ontology region with an ASCII hyphen and the
|
|
26
38
|
* plugin-owned region with an em dash (account-schema-owned-dirs.py:294 vs
|
|
@@ -53,10 +65,11 @@ function allowedBlockOrNull(text) {
|
|
|
53
65
|
|
|
54
66
|
/**
|
|
55
67
|
* @param {string | null} schemaText Contents of accounts/<id>/SCHEMA.md, or null when absent.
|
|
68
|
+
* @param {string[]} [exposeFolders] Operator-named extra top-level folders. Empty or absent = today.
|
|
56
69
|
* @returns {{ schemaPresent: boolean, allowed: string[], domainBuckets: string[],
|
|
57
70
|
* pluginOwned: string[], collisions: string[], exposed: string[] }}
|
|
58
71
|
*/
|
|
59
|
-
export function resolveExposedDirs(schemaText) {
|
|
72
|
+
export function resolveExposedDirs(schemaText, exposeFolders = []) {
|
|
60
73
|
const empty = {
|
|
61
74
|
schemaPresent: false,
|
|
62
75
|
allowed: /** @type {string[]} */ ([]),
|
|
@@ -97,7 +110,14 @@ export function resolveExposedDirs(schemaText) {
|
|
|
97
110
|
const collisions = domainBuckets.filter((d) => pluginOwned.includes(d))
|
|
98
111
|
|
|
99
112
|
const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))]
|
|
100
|
-
const
|
|
113
|
+
const ontologyExposed = [...new Set(candidates)].filter((d) => allowed.includes(d))
|
|
114
|
+
// Extras are unioned AFTER the ontology step and are deliberately NOT
|
|
115
|
+
// intersected with `allowed`: a workflow folder is legitimately outside the
|
|
116
|
+
// ontology set. The charset-plus-deny guard is the whole bound.
|
|
117
|
+
const extras = (Array.isArray(exposeFolders) ? exposeFolders : []).filter(
|
|
118
|
+
(f) => typeof f === 'string' && SAFE_FOLDER.test(f) && !SYSTEM_DIRS.has(f),
|
|
119
|
+
)
|
|
120
|
+
const exposed = [...new Set([...ontologyExposed, ...extras])].sort()
|
|
101
121
|
|
|
102
122
|
return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed }
|
|
103
123
|
}
|
|
@@ -173,4 +173,27 @@ describe('pushAccount', () => {
|
|
|
173
173
|
await push(fakeClient(), { log: (l: string) => lines.push(l) })
|
|
174
174
|
expect(lines.some((l) => l.includes('schemaPresent=false') && l.includes('exposed=none'))).toBe(true)
|
|
175
175
|
})
|
|
176
|
+
|
|
177
|
+
it('op=resolve carries extraFolders and the walk includes an operator folder', async () => {
|
|
178
|
+
// inbound-invoices is NOT in the fixture allowed block, so this also proves
|
|
179
|
+
// an extra bypasses the ontology/allowed intersection.
|
|
180
|
+
file('inbound-invoices/INV-42.pdf')
|
|
181
|
+
const lines: string[] = []
|
|
182
|
+
const c = fakeClient()
|
|
183
|
+
await push(c, { log: (l: string) => lines.push(l), exposeFolders: ['inbound-invoices'] })
|
|
184
|
+
expect(lines.some((l) => /op=resolve .*extraFolders=1/.test(l))).toBe(true)
|
|
185
|
+
expect(lines.some((l) => /exposed=.*inbound-invoices/.test(l))).toBe(true)
|
|
186
|
+
const inserted = c.calls.filter((x) => /INSERT INTO directory/i.test(x.sql)).map((x) => String(x.params[1]))
|
|
187
|
+
expect(inserted).toContain('inbound-invoices/INV-42.pdf')
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
it('with no exposeFolders the operator folder is not walked and extraFolders=0', async () => {
|
|
191
|
+
file('inbound-invoices/INV-42.pdf')
|
|
192
|
+
const lines: string[] = []
|
|
193
|
+
const c = fakeClient()
|
|
194
|
+
await push(c, { log: (l: string) => lines.push(l) })
|
|
195
|
+
const inserted = c.calls.filter((x) => /INSERT INTO directory/i.test(x.sql)).map((x) => String(x.params[1]))
|
|
196
|
+
expect(inserted).not.toContain('inbound-invoices/INV-42.pdf')
|
|
197
|
+
expect(lines.some((l) => /op=resolve .*extraFolders=0/.test(l))).toBe(true)
|
|
198
|
+
})
|
|
176
199
|
})
|
|
@@ -85,4 +85,47 @@ describe('resolveExposedDirs', () => {
|
|
|
85
85
|
const r = resolveExposedDirs('# no fence here')
|
|
86
86
|
expect(r.exposed).toEqual([])
|
|
87
87
|
})
|
|
88
|
+
|
|
89
|
+
it('unions an operator-named folder into exposed', () => {
|
|
90
|
+
const r = resolveExposedDirs(schema({ ontology: ONTOLOGY }), ['inbound-invoices'])
|
|
91
|
+
expect(r.exposed).toEqual(['inbound-invoices', 'output', 'quotes'])
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('exposes an extra folder that is absent from the allowed block', () => {
|
|
95
|
+
// inbound-invoices is deliberately NOT in ALLOWED — extras bypass that intersection.
|
|
96
|
+
const r = resolveExposedDirs(schema({ ontology: ONTOLOGY }), ['inbound-invoices'])
|
|
97
|
+
expect(r.exposed).toContain('inbound-invoices')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('drops a system folder named as an extra', () => {
|
|
101
|
+
const r = resolveExposedDirs(schema({ ontology: ONTOLOGY }), ['secrets'])
|
|
102
|
+
expect(r.exposed).not.toContain('secrets')
|
|
103
|
+
expect(r.exposed).toEqual(['output', 'quotes'])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('drops a traversal or dotted extra', () => {
|
|
107
|
+
const r = resolveExposedDirs(schema({ ontology: ONTOLOGY }), ['jobs/../secrets', '.git', '..'])
|
|
108
|
+
expect(r.exposed).toEqual(['output', 'quotes'])
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
it('an empty extras list equals today', () => {
|
|
112
|
+
const base = resolveExposedDirs(schema({ ontology: ONTOLOGY }))
|
|
113
|
+
expect(resolveExposedDirs(schema({ ontology: ONTOLOGY }), []).exposed).toEqual(base.exposed)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('never applies extras when the schema is missing', () => {
|
|
117
|
+
const r = resolveExposedDirs(null, ['inbound-invoices'])
|
|
118
|
+
expect(r.exposed).toEqual([])
|
|
119
|
+
expect(r.schemaPresent).toBe(false)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('never applies extras when the allowed block is absent', () => {
|
|
123
|
+
const r = resolveExposedDirs('# no fence here', ['inbound-invoices'])
|
|
124
|
+
expect(r.exposed).toEqual([])
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('dedupes an extra that is already an ontology bucket', () => {
|
|
128
|
+
const r = resolveExposedDirs(schema({ ontology: ONTOLOGY }), ['quotes'])
|
|
129
|
+
expect(r.exposed).toEqual(['output', 'quotes'])
|
|
130
|
+
})
|
|
88
131
|
})
|
|
@@ -182,17 +182,27 @@ An account with no `SCHEMA.md` publishes nothing at all. That is deliberate and
|
|
|
182
182
|
what the write-path guard does — the guard fails open so an unseeded account is not write-blocked,
|
|
183
183
|
while a surface facing a client must fail closed.
|
|
184
184
|
|
|
185
|
+
An operator can publish extra folders that are not ontology buckets by listing them in
|
|
186
|
+
`exposeFolders` in the account's `data-portal.json`, for example a workflow folder like
|
|
187
|
+
`inbound-invoices`. Each name must be a single folder matching `[A-Za-z0-9_-]+` and is refused if it
|
|
188
|
+
is a system folder (`agents`, `secrets`, `specialists`, `state`, `memory`, `cache`, `logs`, `tmp`) or
|
|
189
|
+
dot-prefixed. These extras are added on top of the schema-derived set and, unlike ontology buckets,
|
|
190
|
+
are not intersected with the `allowed-top-level` block. An empty or absent list behaves exactly as
|
|
191
|
+
before.
|
|
192
|
+
|
|
185
193
|
The heartbeat runs this hourly for every account carrying a `data-portal.json`. Run it once by hand
|
|
186
194
|
after assembly so the client has something to see on their first sign-in, and check the output names
|
|
187
195
|
the folders you expect:
|
|
188
196
|
|
|
189
197
|
```
|
|
190
|
-
[portal-index] op=resolve account=<id> schemaPresent=true allowed=22 domainBuckets=quotes exposed=output,quotes
|
|
198
|
+
[portal-index] op=resolve account=<id> schemaPresent=true allowed=22 domainBuckets=quotes extraFolders=1 exposed=inbound-invoices,output,quotes
|
|
191
199
|
[portal-index] op=verify account=<id> rowsAfter=41 expected=41
|
|
192
200
|
```
|
|
193
201
|
|
|
194
202
|
`exposed=none` on an account that has deliverables means the schema was missing or its ontology
|
|
195
|
-
region was absent — not that the walk found nothing.
|
|
203
|
+
region was absent — not that the walk found nothing. `extraFolders=<n>` counts the folders the
|
|
204
|
+
operator requested in `exposeFolders`; a non-zero `extraFolders` with none of those names in
|
|
205
|
+
`exposed=` means the guard dropped every one (a system or dot name, or a bad spelling).
|
|
196
206
|
|
|
197
207
|
## How a client browses, downloads and uploads
|
|
198
208
|
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/free-busy-url.test.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-busy-url.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/free-busy-url.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { buildFreeBusyUrl } from '../free-busy-url.js';
|
|
3
|
+
describe('buildFreeBusyUrl', () => {
|
|
4
|
+
it('carries the account id and the escaped from/to', () => {
|
|
5
|
+
const url = buildFreeBusyUrl('19500', 'dddb9148-7ba4-4de7-a0da-6eadc5743eda', '2026-07-21T00:00:00Z', '2026-08-05T00:00:00Z');
|
|
6
|
+
expect(url).toContain('http://127.0.0.1:19500/api/calendar/free-busy');
|
|
7
|
+
expect(url).toContain('account=dddb9148-7ba4-4de7-a0da-6eadc5743eda');
|
|
8
|
+
expect(url).toContain('from=2026-07-21T00%3A00%3A00Z');
|
|
9
|
+
expect(url).toContain('to=2026-08-05T00%3A00%3A00Z');
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=free-busy-url.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-busy-url.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/free-busy-url.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,EAAE,sCAAsC,EAAE,sBAAsB,EAAE,sBAAsB,CAAC,CAAA;QAC7H,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+CAA+C,CAAC,CAAA;QACtE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,8CAA8C,CAAC,CAAA;QACrE,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,+BAA+B,CAAC,CAAA;QACtD,MAAM,CAAC,GAAG,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the device-local free/busy URL the publish dispatcher fetches. The
|
|
3
|
+
* `account` param names which booking-site account's free/busy to compute, so a
|
|
4
|
+
* multi-tenant install refreshes every site rather than only the one the server
|
|
5
|
+
* resolves by default (Task 1890). Values are URL-encoded for the query string.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildFreeBusyUrl(port: string, accountId: string, from: string, to: string): string;
|
|
8
|
+
//# sourceMappingURL=free-busy-url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-busy-url.d.ts","sourceRoot":"","sources":["../../src/lib/free-busy-url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAMlG"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the device-local free/busy URL the publish dispatcher fetches. The
|
|
3
|
+
* `account` param names which booking-site account's free/busy to compute, so a
|
|
4
|
+
* multi-tenant install refreshes every site rather than only the one the server
|
|
5
|
+
* resolves by default (Task 1890). Values are URL-encoded for the query string.
|
|
6
|
+
*/
|
|
7
|
+
export function buildFreeBusyUrl(port, accountId, from, to) {
|
|
8
|
+
return (`http://127.0.0.1:${port}/api/calendar/free-busy` +
|
|
9
|
+
`?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}` +
|
|
10
|
+
`&account=${encodeURIComponent(accountId)}`);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=free-busy-url.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"free-busy-url.js","sourceRoot":"","sources":["../../src/lib/free-busy-url.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,SAAiB,EAAE,IAAY,EAAE,EAAU;IACxF,OAAO,CACL,oBAAoB,IAAI,yBAAyB;QACjD,SAAS,kBAAkB,CAAC,IAAI,CAAC,OAAO,kBAAkB,CAAC,EAAE,CAAC,EAAE;QAChE,YAAY,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAC5C,CAAA;AACH,CAAC"}
|
|
@@ -28,6 +28,7 @@ import { readFileSync, writeFileSync, existsSync, unlinkSync, mkdirSync, mkdtemp
|
|
|
28
28
|
import { resolve, join, basename } from "node:path";
|
|
29
29
|
import { homedir, tmpdir } from "node:os";
|
|
30
30
|
import { resolveHouseScopedToken } from "../lib/house-scoped-token.js";
|
|
31
|
+
import { buildFreeBusyUrl } from "../lib/free-busy-url.js";
|
|
31
32
|
function resolveConfigDir() {
|
|
32
33
|
const platformRoot = process.env.PLATFORM_ROOT;
|
|
33
34
|
if (!platformRoot)
|
|
@@ -41,7 +42,14 @@ function resolveConfigDir() {
|
|
|
41
42
|
return brand.configDir;
|
|
42
43
|
}
|
|
43
44
|
const CONFIG_DIR = resolve(homedir(), resolveConfigDir());
|
|
44
|
-
|
|
45
|
+
// Per-account lock (Task 1890): the server spawns one dispatcher per booking-site
|
|
46
|
+
// account each tick, so a single shared lock would make all but one bail
|
|
47
|
+
// result=skipped reason=lock-held. Keyed on the account this run publishes,
|
|
48
|
+
// resolved the same way as `accountId` in main() (env, else the account dir's
|
|
49
|
+
// basename) so a run with only PUBLISH_ACCOUNT_DIR set still locks per account.
|
|
50
|
+
const LOCK_ACCOUNT = process.env.PUBLISH_ACCOUNT_ID ??
|
|
51
|
+
(process.env.PUBLISH_ACCOUNT_DIR ? basename(process.env.PUBLISH_ACCOUNT_DIR) : "default");
|
|
52
|
+
const LOCK_FILE = resolve(CONFIG_DIR, `publish-availability-${LOCK_ACCOUNT}.lock`);
|
|
45
53
|
function acquireLock() {
|
|
46
54
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
47
55
|
if (existsSync(LOCK_FILE)) {
|
|
@@ -182,7 +190,9 @@ async function main() {
|
|
|
182
190
|
const from = utcMidnight(now, 0);
|
|
183
191
|
const to = utcMidnight(now, daysAhead + 1);
|
|
184
192
|
const nowIso = now.toISOString();
|
|
185
|
-
|
|
193
|
+
// Name the account so a multi-tenant install computes this site's free/busy,
|
|
194
|
+
// not whatever the endpoint resolves by default (Task 1890).
|
|
195
|
+
const url = buildFreeBusyUrl(port, accountId, from, to);
|
|
186
196
|
let payload;
|
|
187
197
|
try {
|
|
188
198
|
// Bound the read so a wedged server cannot hold the PID lock.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-availability.js","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC9G,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"publish-availability.js","sourceRoot":"","sources":["../../src/scripts/publish-availability.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-dir-schema-reconcile.d.ts","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"account-dir-schema-reconcile.d.ts","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"AA2FA,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;+DAE+D;AAC/D,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAE9D;AAED;;8EAE8E;AAC9E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAcpE;AA0FD;;;gFAGgF;AAChF,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,MAAM,GAAG,eAAe,CAwDvF;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED;;;oCAGoC;AACpC,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAwBtD;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,IAAI,CAAA;CACb;AAED;oDACoD;AACpD,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,aAAa,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,GAC3C,yBAAyB,CAiB3B"}
|
package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js
CHANGED
|
@@ -37,7 +37,17 @@ const QUARANTINE_DIR = '.quarantine';
|
|
|
37
37
|
* an account carrying a stale/older-template/hand-edited schema whose allowed
|
|
38
38
|
* block omits one of these must not have it moved. This is a fail-SAFE belt on
|
|
39
39
|
* the control plane, the mirror of the guard's fail-OPEN on a missing schema —
|
|
40
|
-
* operator strays are still quarantined; the account's own machinery is not.
|
|
40
|
+
* operator strays are still quarantined; the account's own machinery is not.
|
|
41
|
+
*
|
|
42
|
+
* The second group is platform-written control-plane FILES the platform reads
|
|
43
|
+
* and writes at the account root every run. Unlike the tool-owned buckets, no
|
|
44
|
+
* plugin declares them into the SCHEMA.md allowed-top-level block, so without
|
|
45
|
+
* this belt the sweep quarantines them and the feature that reads each one goes
|
|
46
|
+
* dark: calendar-availability.json is the booking page's availability config (a
|
|
47
|
+
* quarantine froze a live booking page, Task 1877 fix); wa/webchat/telegram
|
|
48
|
+
* -channel-bindings.json route each channel's inbound; canonical-webchat-session
|
|
49
|
+
* .json pins the webchat session; session-titles.json holds operator renames.
|
|
50
|
+
* Each is `join(accountDir, <name>)` in platform code, none is operator data. */
|
|
41
51
|
const PROTECTED_TOP_LEVEL = new Set([
|
|
42
52
|
'.git',
|
|
43
53
|
'.claude',
|
|
@@ -45,6 +55,12 @@ const PROTECTED_TOP_LEVEL = new Set([
|
|
|
45
55
|
'SCHEMA.md',
|
|
46
56
|
'account.json',
|
|
47
57
|
'AGENTS.md',
|
|
58
|
+
'calendar-availability.json',
|
|
59
|
+
'wa-channel-bindings.json',
|
|
60
|
+
'webchat-channel-bindings.json',
|
|
61
|
+
'telegram-channel-bindings.json',
|
|
62
|
+
'canonical-webchat-session.json',
|
|
63
|
+
'session-titles.json',
|
|
48
64
|
]);
|
|
49
65
|
/** Buckets whose depth fs-schema-guard.sh checks, with the same 3-segment cap.
|
|
50
66
|
* Every other top-level bucket is tool-owned and unbounded — not walked. This
|
package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account-dir-schema-reconcile.js","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,gFAAgF;AAChF,8EAA8E;AAC9E,iFAAiF;AACjF,iFAAiF;AACjF,kFAAkF;AAClF,kDAAkD;AAClD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,gFAAgF;AAChF,gFAAgF;AAChF,+EAA+E;AAC/E,gFAAgF;AAChF,+CAA+C;AAC/C,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,+EAA+E;AAC/E,iFAAiF;AACjF,2CAA2C;AAE3C,OAAO,EACL,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACZ,UAAU,EACV,cAAc,EACd,QAAQ,GACT,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AAE/C,MAAM,GAAG,GAAG,gBAAgB,CAAA;AAC5B,MAAM,cAAc,GAAG,aAAa,CAAA;AAEpC
|
|
1
|
+
{"version":3,"file":"account-dir-schema-reconcile.js","sourceRoot":"","sources":["../src/account-dir-schema-reconcile.ts"],"names":[],"mappings":"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"}
|
package/payload/server/server.js
CHANGED
|
@@ -1643,10 +1643,10 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
1643
1643
|
};
|
|
1644
1644
|
|
|
1645
1645
|
// server/index.ts
|
|
1646
|
-
import { readFileSync as
|
|
1646
|
+
import { readFileSync as readFileSync43, existsSync as existsSync39, watchFile, readdirSync as readdirSync26, statSync as statSync17 } from "fs";
|
|
1647
1647
|
import { spawn as spawn3 } from "child_process";
|
|
1648
1648
|
import { createHash as createHash7 } from "crypto";
|
|
1649
|
-
import { resolve as
|
|
1649
|
+
import { resolve as resolve39, join as join45, basename as basename12 } from "path";
|
|
1650
1650
|
import { homedir as homedir4 } from "os";
|
|
1651
1651
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
1652
1652
|
|
|
@@ -2920,7 +2920,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
2920
2920
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
2921
2921
|
console.error(`${TAG6} draining credential save queue\u2026`);
|
|
2922
2922
|
const timer = new Promise(
|
|
2923
|
-
(
|
|
2923
|
+
(resolve40) => setTimeout(() => resolve40("timeout"), timeoutMs)
|
|
2924
2924
|
);
|
|
2925
2925
|
const result = await Promise.race([
|
|
2926
2926
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -3097,11 +3097,11 @@ async function createWaSocket(opts) {
|
|
|
3097
3097
|
return sock;
|
|
3098
3098
|
}
|
|
3099
3099
|
async function waitForConnection(sock) {
|
|
3100
|
-
return new Promise((
|
|
3100
|
+
return new Promise((resolve40, reject) => {
|
|
3101
3101
|
const handler = (update) => {
|
|
3102
3102
|
if (update.connection === "open") {
|
|
3103
3103
|
sock.ev.off("connection.update", handler);
|
|
3104
|
-
|
|
3104
|
+
resolve40();
|
|
3105
3105
|
}
|
|
3106
3106
|
if (update.connection === "close") {
|
|
3107
3107
|
sock.ev.off("connection.update", handler);
|
|
@@ -3209,7 +3209,7 @@ function isTimeoutError(err) {
|
|
|
3209
3209
|
return Boolean(err?.__maxyTimeout);
|
|
3210
3210
|
}
|
|
3211
3211
|
function withTimeout(label, promise, timeoutMs) {
|
|
3212
|
-
return new Promise((
|
|
3212
|
+
return new Promise((resolve40, reject) => {
|
|
3213
3213
|
const timer = setTimeout(() => {
|
|
3214
3214
|
const err = new Error(`${label} timed out after ${timeoutMs}ms`);
|
|
3215
3215
|
err.__maxyTimeout = true;
|
|
@@ -3219,7 +3219,7 @@ function withTimeout(label, promise, timeoutMs) {
|
|
|
3219
3219
|
promise.then(
|
|
3220
3220
|
(value) => {
|
|
3221
3221
|
clearTimeout(timer);
|
|
3222
|
-
|
|
3222
|
+
resolve40(value);
|
|
3223
3223
|
},
|
|
3224
3224
|
(err) => {
|
|
3225
3225
|
clearTimeout(timer);
|
|
@@ -4278,8 +4278,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
4278
4278
|
const { givenName, familyName } = splitName(input.pushName);
|
|
4279
4279
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
4280
4280
|
let release;
|
|
4281
|
-
const mine = new Promise((
|
|
4282
|
-
release =
|
|
4281
|
+
const mine = new Promise((resolve40) => {
|
|
4282
|
+
release = resolve40;
|
|
4283
4283
|
});
|
|
4284
4284
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
4285
4285
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -6016,11 +6016,11 @@ async function connectWithReconnect(conn) {
|
|
|
6016
6016
|
console.error(
|
|
6017
6017
|
`${TAG17} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
6018
6018
|
);
|
|
6019
|
-
await new Promise((
|
|
6020
|
-
const timer = setTimeout(
|
|
6019
|
+
await new Promise((resolve40) => {
|
|
6020
|
+
const timer = setTimeout(resolve40, delay);
|
|
6021
6021
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
6022
6022
|
clearTimeout(timer);
|
|
6023
|
-
|
|
6023
|
+
resolve40();
|
|
6024
6024
|
}, { once: true });
|
|
6025
6025
|
});
|
|
6026
6026
|
}
|
|
@@ -6028,16 +6028,16 @@ async function connectWithReconnect(conn) {
|
|
|
6028
6028
|
}
|
|
6029
6029
|
}
|
|
6030
6030
|
function waitForDisconnectEvent(conn) {
|
|
6031
|
-
return new Promise((
|
|
6031
|
+
return new Promise((resolve40) => {
|
|
6032
6032
|
if (!conn.sock) {
|
|
6033
|
-
|
|
6033
|
+
resolve40();
|
|
6034
6034
|
return;
|
|
6035
6035
|
}
|
|
6036
6036
|
const sock = conn.sock;
|
|
6037
6037
|
const handler = (update) => {
|
|
6038
6038
|
if (update.connection === "close") {
|
|
6039
6039
|
sock.ev.off("connection.update", handler);
|
|
6040
|
-
|
|
6040
|
+
resolve40();
|
|
6041
6041
|
}
|
|
6042
6042
|
};
|
|
6043
6043
|
sock.ev.on("connection.update", handler);
|
|
@@ -6511,8 +6511,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
6511
6511
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
6512
6512
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
6513
6513
|
let resolvePending;
|
|
6514
|
-
const sttPending = new Promise((
|
|
6515
|
-
resolvePending =
|
|
6514
|
+
const sttPending = new Promise((resolve40) => {
|
|
6515
|
+
resolvePending = resolve40;
|
|
6516
6516
|
});
|
|
6517
6517
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
6518
6518
|
try {
|
|
@@ -6901,20 +6901,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
6901
6901
|
|
|
6902
6902
|
// server/routes/health.ts
|
|
6903
6903
|
function checkPort(port2, timeoutMs = 500) {
|
|
6904
|
-
return new Promise((
|
|
6904
|
+
return new Promise((resolve40) => {
|
|
6905
6905
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
6906
6906
|
socket.setTimeout(timeoutMs);
|
|
6907
6907
|
socket.once("connect", () => {
|
|
6908
6908
|
socket.destroy();
|
|
6909
|
-
|
|
6909
|
+
resolve40(true);
|
|
6910
6910
|
});
|
|
6911
6911
|
socket.once("error", () => {
|
|
6912
6912
|
socket.destroy();
|
|
6913
|
-
|
|
6913
|
+
resolve40(false);
|
|
6914
6914
|
});
|
|
6915
6915
|
socket.once("timeout", () => {
|
|
6916
6916
|
socket.destroy();
|
|
6917
|
-
|
|
6917
|
+
resolve40(false);
|
|
6918
6918
|
});
|
|
6919
6919
|
});
|
|
6920
6920
|
}
|
|
@@ -8245,8 +8245,8 @@ async function startLogin(opts) {
|
|
|
8245
8245
|
await clearAuth(authDir);
|
|
8246
8246
|
let resolveCode = null;
|
|
8247
8247
|
let rejectCode = null;
|
|
8248
|
-
const codePromise = new Promise((
|
|
8249
|
-
resolveCode =
|
|
8248
|
+
const codePromise = new Promise((resolve40, reject) => {
|
|
8249
|
+
resolveCode = resolve40;
|
|
8250
8250
|
rejectCode = reject;
|
|
8251
8251
|
});
|
|
8252
8252
|
const codeTimer = setTimeout(
|
|
@@ -14681,15 +14681,15 @@ function operatorRoleFor(config, userId) {
|
|
|
14681
14681
|
async function buildAccountOptionList(accounts, userId) {
|
|
14682
14682
|
const houseAccount = accounts.find((a) => a.config.role === "house");
|
|
14683
14683
|
console.log(`[admin-accounts] op=list count=${accounts.length} house=${houseAccount?.accountId ?? "none"}`);
|
|
14684
|
-
const
|
|
14684
|
+
const resolve40 = async (a) => {
|
|
14685
14685
|
const businessName = await fetchAccountName(a.accountId) || void 0;
|
|
14686
14686
|
return { accountId: a.accountId, businessName, role: operatorRoleFor(a.config, userId), isHouse: a.config.role === "house" };
|
|
14687
14687
|
};
|
|
14688
14688
|
const houseOptions = await Promise.all(
|
|
14689
|
-
accounts.filter((a) => a.config.role === "house").map(
|
|
14689
|
+
accounts.filter((a) => a.config.role === "house").map(resolve40)
|
|
14690
14690
|
);
|
|
14691
14691
|
const nonHouseOptions = await Promise.all(
|
|
14692
|
-
accounts.filter((a) => a.config.role !== "house").map(
|
|
14692
|
+
accounts.filter((a) => a.config.role !== "house").map(resolve40)
|
|
14693
14693
|
);
|
|
14694
14694
|
const sortKey = (o) => (o.businessName || o.accountId).toLowerCase();
|
|
14695
14695
|
nonHouseOptions.sort((x, y) => {
|
|
@@ -24494,7 +24494,17 @@ app62.get("/free-busy", async (c) => {
|
|
|
24494
24494
|
if (Number.isNaN(Date.parse(from)) || Number.isNaN(Date.parse(to))) {
|
|
24495
24495
|
return c.json({ error: "from and to must be parseable ISO timestamps" }, 400);
|
|
24496
24496
|
}
|
|
24497
|
-
const
|
|
24497
|
+
const accountParam = c.req.query("account");
|
|
24498
|
+
let account;
|
|
24499
|
+
if (accountParam) {
|
|
24500
|
+
account = listValidAccounts().find((a) => a.accountId === accountParam) ?? null;
|
|
24501
|
+
if (!account) {
|
|
24502
|
+
console.error(`[calendar] op=free-busy-unknown-account account="${accountParam}"`);
|
|
24503
|
+
return c.json({ error: "Availability not configured" }, 404);
|
|
24504
|
+
}
|
|
24505
|
+
} else {
|
|
24506
|
+
account = resolveAccount();
|
|
24507
|
+
}
|
|
24498
24508
|
if (!account) return c.json({ error: "No account configured" }, 500);
|
|
24499
24509
|
let config;
|
|
24500
24510
|
try {
|
|
@@ -24546,6 +24556,8 @@ var ONT_END = "<!-- ontology-buckets:end -->";
|
|
|
24546
24556
|
var OWNED_START = "<!-- plugin-owned-dirs:start -->";
|
|
24547
24557
|
var OWNED_END = "<!-- plugin-owned-dirs:end -->";
|
|
24548
24558
|
var ALWAYS_EXPOSED = "output";
|
|
24559
|
+
var SYSTEM_DIRS = /* @__PURE__ */ new Set(["agents", "secrets", "specialists", "state", "memory", "cache", "logs", "tmp"]);
|
|
24560
|
+
var SAFE_FOLDER = /^[A-Za-z0-9_-]+$/;
|
|
24549
24561
|
var DOMAIN_LINE = /^- `([^`/]+)\/` - one folder per /;
|
|
24550
24562
|
var OWNED_LINE = /^- `([^`/]+)\/` — /;
|
|
24551
24563
|
function region(text, start, end) {
|
|
@@ -24565,7 +24577,7 @@ function allowedBlockOrNull(text) {
|
|
|
24565
24577
|
}
|
|
24566
24578
|
return null;
|
|
24567
24579
|
}
|
|
24568
|
-
function resolveExposedDirs(schemaText) {
|
|
24580
|
+
function resolveExposedDirs(schemaText, exposeFolders = []) {
|
|
24569
24581
|
const empty = {
|
|
24570
24582
|
schemaPresent: false,
|
|
24571
24583
|
allowed: (
|
|
@@ -24607,7 +24619,11 @@ function resolveExposedDirs(schemaText) {
|
|
|
24607
24619
|
const pluginOwned = dirsIn(owned, OWNED_LINE);
|
|
24608
24620
|
const collisions = domainBuckets.filter((d) => pluginOwned.includes(d));
|
|
24609
24621
|
const candidates = [ALWAYS_EXPOSED, ...domainBuckets.filter((d) => !collisions.includes(d))];
|
|
24610
|
-
const
|
|
24622
|
+
const ontologyExposed = [...new Set(candidates)].filter((d) => allowed.includes(d));
|
|
24623
|
+
const extras = (Array.isArray(exposeFolders) ? exposeFolders : []).filter(
|
|
24624
|
+
(f) => typeof f === "string" && SAFE_FOLDER.test(f) && !SYSTEM_DIRS.has(f)
|
|
24625
|
+
);
|
|
24626
|
+
const exposed = [.../* @__PURE__ */ new Set([...ontologyExposed, ...extras])].sort();
|
|
24611
24627
|
return { schemaPresent: true, allowed, domainBuckets, pluginOwned, collisions, exposed };
|
|
24612
24628
|
}
|
|
24613
24629
|
|
|
@@ -24656,6 +24672,14 @@ async function readAccountSecret(accountId) {
|
|
|
24656
24672
|
function isValidAccountId2(value) {
|
|
24657
24673
|
return /^[A-Za-z0-9-]+$/.test(value);
|
|
24658
24674
|
}
|
|
24675
|
+
async function readExposeFolders(accountDir) {
|
|
24676
|
+
try {
|
|
24677
|
+
const cfg = JSON.parse(await readFile6(join42(accountDir, "data-portal.json"), "utf8"));
|
|
24678
|
+
return Array.isArray(cfg?.exposeFolders) ? cfg.exposeFolders.filter((f) => typeof f === "string") : [];
|
|
24679
|
+
} catch {
|
|
24680
|
+
return [];
|
|
24681
|
+
}
|
|
24682
|
+
}
|
|
24659
24683
|
async function resolveFetchTarget(accountDir, relPath) {
|
|
24660
24684
|
let schemaText = null;
|
|
24661
24685
|
try {
|
|
@@ -24663,7 +24687,8 @@ async function resolveFetchTarget(accountDir, relPath) {
|
|
|
24663
24687
|
} catch {
|
|
24664
24688
|
schemaText = null;
|
|
24665
24689
|
}
|
|
24666
|
-
const
|
|
24690
|
+
const exposeFolders = await readExposeFolders(accountDir);
|
|
24691
|
+
const { exposed } = resolveExposedDirs(schemaText, exposeFolders);
|
|
24667
24692
|
if (exposed.length === 0) return { ok: false, reason: "not-exposed" };
|
|
24668
24693
|
const lexical = resolve32(accountDir, relPath);
|
|
24669
24694
|
let root;
|
|
@@ -26484,16 +26509,16 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26484
26509
|
* The public /api/chat route awaits this to return the reply on the same SSE
|
|
26485
26510
|
* response, preserving the pre-756 POST→single-blob browser contract. */
|
|
26486
26511
|
awaitReply(key, timeoutMs) {
|
|
26487
|
-
return new Promise((
|
|
26512
|
+
return new Promise((resolve40) => {
|
|
26488
26513
|
const timer = setTimeout(() => {
|
|
26489
26514
|
this.replyAwaiters.delete(key);
|
|
26490
|
-
|
|
26515
|
+
resolve40({ timeout: true });
|
|
26491
26516
|
}, timeoutMs);
|
|
26492
26517
|
if (timer.unref) timer.unref();
|
|
26493
26518
|
this.replyAwaiters.set(key, (r) => {
|
|
26494
26519
|
clearTimeout(timer);
|
|
26495
26520
|
this.replyAwaiters.delete(key);
|
|
26496
|
-
|
|
26521
|
+
resolve40(r);
|
|
26497
26522
|
});
|
|
26498
26523
|
});
|
|
26499
26524
|
}
|
|
@@ -26504,11 +26529,11 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26504
26529
|
* terminal dialog stays answerable). */
|
|
26505
26530
|
awaitPermissionVerdict(p, timeoutMs) {
|
|
26506
26531
|
const k = _WebchatGateway.promptKey(p.key, p.requestId);
|
|
26507
|
-
return new Promise((
|
|
26532
|
+
return new Promise((resolve40) => {
|
|
26508
26533
|
this.pendingPrompts.get(k)?.resolve({ timeout: true });
|
|
26509
26534
|
const timer = setTimeout(() => {
|
|
26510
26535
|
this.pendingPrompts.delete(k);
|
|
26511
|
-
|
|
26536
|
+
resolve40({ timeout: true });
|
|
26512
26537
|
}, timeoutMs);
|
|
26513
26538
|
if (timer.unref) timer.unref();
|
|
26514
26539
|
this.pendingPrompts.set(k, {
|
|
@@ -26517,7 +26542,7 @@ var WebchatGateway = class _WebchatGateway {
|
|
|
26517
26542
|
resolve: (r) => {
|
|
26518
26543
|
clearTimeout(timer);
|
|
26519
26544
|
this.pendingPrompts.delete(k);
|
|
26520
|
-
|
|
26545
|
+
resolve40(r);
|
|
26521
26546
|
}
|
|
26522
26547
|
});
|
|
26523
26548
|
console.error(`[webchat:perm] op=open key=${keyDisplay(p.key)} id=${p.requestId} tool=${p.toolName}`);
|
|
@@ -28141,6 +28166,22 @@ function warnOnChannelAdminBindingDrift() {
|
|
|
28141
28166
|
}
|
|
28142
28167
|
}
|
|
28143
28168
|
|
|
28169
|
+
// server/lib/booking-site-accounts.ts
|
|
28170
|
+
import { existsSync as existsSync37, readFileSync as readFileSync41 } from "fs";
|
|
28171
|
+
import { resolve as resolve37 } from "path";
|
|
28172
|
+
function listBookingSiteAccounts(accounts) {
|
|
28173
|
+
return accounts.filter((a) => {
|
|
28174
|
+
const availPath = resolve37(a.accountDir, "calendar-availability.json");
|
|
28175
|
+
if (!existsSync37(availPath)) return false;
|
|
28176
|
+
try {
|
|
28177
|
+
const cfg = JSON.parse(readFileSync41(availPath, "utf-8"));
|
|
28178
|
+
return typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
28179
|
+
} catch {
|
|
28180
|
+
return false;
|
|
28181
|
+
}
|
|
28182
|
+
});
|
|
28183
|
+
}
|
|
28184
|
+
|
|
28144
28185
|
// app/lib/admin-sse-registry.ts
|
|
28145
28186
|
var activeAdminSSEControllers = /* @__PURE__ */ new Set();
|
|
28146
28187
|
function broadcastAdminShutdown(reason) {
|
|
@@ -28171,8 +28212,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
28171
28212
|
|
|
28172
28213
|
// ../lib/entitlement/src/index.ts
|
|
28173
28214
|
import { createPublicKey, createHash as createHash6, verify as cryptoVerify } from "crypto";
|
|
28174
|
-
import { existsSync as
|
|
28175
|
-
import { resolve as
|
|
28215
|
+
import { existsSync as existsSync38, readFileSync as readFileSync42, statSync as statSync16 } from "fs";
|
|
28216
|
+
import { resolve as resolve38 } from "path";
|
|
28176
28217
|
|
|
28177
28218
|
// ../lib/entitlement/src/canonicalize.ts
|
|
28178
28219
|
function canonicalize(value) {
|
|
@@ -28207,7 +28248,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
28207
28248
|
var GRACE_DAYS = 7;
|
|
28208
28249
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
28209
28250
|
function pubkeyPath(brand) {
|
|
28210
|
-
return
|
|
28251
|
+
return resolve38(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
28211
28252
|
}
|
|
28212
28253
|
var memo = null;
|
|
28213
28254
|
function memoKey(mtimeMs, account) {
|
|
@@ -28219,8 +28260,8 @@ function resolveEntitlement(brand, account) {
|
|
|
28219
28260
|
if (brand.commercialMode !== true) {
|
|
28220
28261
|
return logResolved(implicitTrust(account), null);
|
|
28221
28262
|
}
|
|
28222
|
-
const entitlementPath =
|
|
28223
|
-
if (!
|
|
28263
|
+
const entitlementPath = resolve38(brand.configDir, "entitlement.json");
|
|
28264
|
+
if (!existsSync38(entitlementPath)) {
|
|
28224
28265
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
28225
28266
|
}
|
|
28226
28267
|
const stat9 = statSync16(entitlementPath);
|
|
@@ -28235,7 +28276,7 @@ function resolveEntitlement(brand, account) {
|
|
|
28235
28276
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
28236
28277
|
let pubkeyPem;
|
|
28237
28278
|
try {
|
|
28238
|
-
pubkeyPem =
|
|
28279
|
+
pubkeyPem = readFileSync42(pubkeyPath(brand), "utf-8");
|
|
28239
28280
|
} catch (err) {
|
|
28240
28281
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
28241
28282
|
reason: "pubkey-missing"
|
|
@@ -28249,7 +28290,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
28249
28290
|
}
|
|
28250
28291
|
let envelope;
|
|
28251
28292
|
try {
|
|
28252
|
-
envelope = JSON.parse(
|
|
28293
|
+
envelope = JSON.parse(readFileSync42(entitlementPath, "utf-8"));
|
|
28253
28294
|
} catch {
|
|
28254
28295
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
28255
28296
|
}
|
|
@@ -28413,12 +28454,12 @@ installMediaDownloadGuard();
|
|
|
28413
28454
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
28414
28455
|
var BRAND_JSON_PATH = PLATFORM_ROOT8 ? join45(PLATFORM_ROOT8, "config", "brand.json") : "";
|
|
28415
28456
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
28416
|
-
if (BRAND_JSON_PATH && !
|
|
28457
|
+
if (BRAND_JSON_PATH && !existsSync39(BRAND_JSON_PATH)) {
|
|
28417
28458
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
28418
28459
|
}
|
|
28419
|
-
if (BRAND_JSON_PATH &&
|
|
28460
|
+
if (BRAND_JSON_PATH && existsSync39(BRAND_JSON_PATH)) {
|
|
28420
28461
|
try {
|
|
28421
|
-
const parsed = JSON.parse(
|
|
28462
|
+
const parsed = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
|
|
28422
28463
|
BRAND = { ...BRAND, ...parsed };
|
|
28423
28464
|
} catch (err) {
|
|
28424
28465
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -28453,8 +28494,8 @@ var brandLoginOpts = {
|
|
|
28453
28494
|
var ALIAS_DOMAINS_PATH = join45(homedir4(), BRAND.configDir, "alias-domains.json");
|
|
28454
28495
|
function loadAliasDomains() {
|
|
28455
28496
|
try {
|
|
28456
|
-
if (!
|
|
28457
|
-
const parsed = JSON.parse(
|
|
28497
|
+
if (!existsSync39(ALIAS_DOMAINS_PATH)) return null;
|
|
28498
|
+
const parsed = JSON.parse(readFileSync43(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
28458
28499
|
if (!Array.isArray(parsed)) {
|
|
28459
28500
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
28460
28501
|
return null;
|
|
@@ -28527,7 +28568,7 @@ var waGateway = new WaGateway({
|
|
|
28527
28568
|
fetchStandingRules: fetchAccountStandingRules,
|
|
28528
28569
|
resolveSessionOverride: (accountId, senderId) => resolveChannelOverride(accountId, "whatsapp", senderId),
|
|
28529
28570
|
gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
|
|
28530
|
-
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ??
|
|
28571
|
+
serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
|
|
28531
28572
|
// Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
|
|
28532
28573
|
// path-validation scope) is the sender's effective SESSION account, resolved
|
|
28533
28574
|
// once at the inbound gate and threaded here via the gateway's per-sender doc
|
|
@@ -28542,7 +28583,7 @@ var waGateway = new WaGateway({
|
|
|
28542
28583
|
caption,
|
|
28543
28584
|
accountId,
|
|
28544
28585
|
maxyAccountId,
|
|
28545
|
-
platformRoot:
|
|
28586
|
+
platformRoot: resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, ".."))
|
|
28546
28587
|
});
|
|
28547
28588
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
28548
28589
|
},
|
|
@@ -29218,20 +29259,20 @@ app64.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
29218
29259
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
29219
29260
|
return c.text("Not found", 404);
|
|
29220
29261
|
}
|
|
29221
|
-
const filePath =
|
|
29222
|
-
const expectedDir =
|
|
29262
|
+
const filePath = resolve39(account.accountDir, "agents", slug, "assets", filename);
|
|
29263
|
+
const expectedDir = resolve39(account.accountDir, "agents", slug, "assets");
|
|
29223
29264
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
29224
29265
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
29225
29266
|
return c.text("Forbidden", 403);
|
|
29226
29267
|
}
|
|
29227
|
-
if (!
|
|
29268
|
+
if (!existsSync39(filePath)) {
|
|
29228
29269
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
29229
29270
|
return c.text("Not found", 404);
|
|
29230
29271
|
}
|
|
29231
29272
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
29232
29273
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
29233
29274
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
29234
|
-
const body =
|
|
29275
|
+
const body = readFileSync43(filePath);
|
|
29235
29276
|
return c.body(body, 200, {
|
|
29236
29277
|
"Content-Type": contentType,
|
|
29237
29278
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -29248,20 +29289,20 @@ app64.get("/generated/:filename", (c) => {
|
|
|
29248
29289
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
29249
29290
|
return c.text("Not found", 404);
|
|
29250
29291
|
}
|
|
29251
|
-
const filePath =
|
|
29252
|
-
const expectedDir =
|
|
29292
|
+
const filePath = resolve39(account.accountDir, "generated", filename);
|
|
29293
|
+
const expectedDir = resolve39(account.accountDir, "generated");
|
|
29253
29294
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
29254
29295
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
29255
29296
|
return c.text("Forbidden", 403);
|
|
29256
29297
|
}
|
|
29257
|
-
if (!
|
|
29298
|
+
if (!existsSync39(filePath)) {
|
|
29258
29299
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
29259
29300
|
return c.text("Not found", 404);
|
|
29260
29301
|
}
|
|
29261
29302
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
29262
29303
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
29263
29304
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
29264
|
-
const body =
|
|
29305
|
+
const body = readFileSync43(filePath);
|
|
29265
29306
|
return c.body(body, 200, {
|
|
29266
29307
|
"Content-Type": contentType,
|
|
29267
29308
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -29274,9 +29315,9 @@ app64.route("/v", visitor_consent_default);
|
|
|
29274
29315
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
29275
29316
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
29276
29317
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
29277
|
-
if (BRAND_JSON_PATH &&
|
|
29318
|
+
if (BRAND_JSON_PATH && existsSync39(BRAND_JSON_PATH)) {
|
|
29278
29319
|
try {
|
|
29279
|
-
const fullBrand = JSON.parse(
|
|
29320
|
+
const fullBrand = JSON.parse(readFileSync43(BRAND_JSON_PATH, "utf-8"));
|
|
29280
29321
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
29281
29322
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
29282
29323
|
} catch {
|
|
@@ -29303,11 +29344,11 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
29303
29344
|
var brandThemeColor = BRAND.defaultColors?.primary ?? "#000000";
|
|
29304
29345
|
var brandBackgroundColor = BRAND.defaultColors?.background ?? "#ffffff";
|
|
29305
29346
|
var brandAppIconsExist = [brandAppIcon192Path, brandAppIcon512Path, brandMaskableIconPath].every(
|
|
29306
|
-
(p) =>
|
|
29347
|
+
(p) => existsSync39(resolve39(process.cwd(), "public", p.replace(/^\//, "")))
|
|
29307
29348
|
);
|
|
29308
29349
|
var SW_SOURCE = (() => {
|
|
29309
29350
|
try {
|
|
29310
|
-
return
|
|
29351
|
+
return readFileSync43(resolve39(process.cwd(), "public", "sw.js"), "utf-8");
|
|
29311
29352
|
} catch {
|
|
29312
29353
|
return null;
|
|
29313
29354
|
}
|
|
@@ -29316,8 +29357,8 @@ function readInstalledVersion() {
|
|
|
29316
29357
|
try {
|
|
29317
29358
|
if (!PLATFORM_ROOT8) return "unknown";
|
|
29318
29359
|
const versionFile = join45(PLATFORM_ROOT8, "config", `.${BRAND.hostname}-version`);
|
|
29319
|
-
if (!
|
|
29320
|
-
const content =
|
|
29360
|
+
if (!existsSync39(versionFile)) return "unknown";
|
|
29361
|
+
const content = readFileSync43(versionFile, "utf-8").trim();
|
|
29321
29362
|
return content || "unknown";
|
|
29322
29363
|
} catch {
|
|
29323
29364
|
return "unknown";
|
|
@@ -29358,7 +29399,7 @@ var clientErrorReporterScript = `<script>
|
|
|
29358
29399
|
function cachedHtml(file) {
|
|
29359
29400
|
let html = htmlCache.get(file);
|
|
29360
29401
|
if (!html) {
|
|
29361
|
-
html =
|
|
29402
|
+
html = readFileSync43(resolve39(process.cwd(), "public", file), "utf-8");
|
|
29362
29403
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
29363
29404
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
29364
29405
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -29380,8 +29421,8 @@ function loadBrandingCache(agentSlug) {
|
|
|
29380
29421
|
const accountId = getDefaultAccountId();
|
|
29381
29422
|
if (!accountId) return null;
|
|
29382
29423
|
const cachePath = join45(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
29383
|
-
if (!
|
|
29384
|
-
return JSON.parse(
|
|
29424
|
+
if (!existsSync39(cachePath)) return null;
|
|
29425
|
+
return JSON.parse(readFileSync43(cachePath, "utf-8"));
|
|
29385
29426
|
} catch {
|
|
29386
29427
|
return null;
|
|
29387
29428
|
}
|
|
@@ -29476,7 +29517,7 @@ app64.use("/vnc-popout.html", logViewerFetch);
|
|
|
29476
29517
|
app64.get("/vnc-popout.html", (c) => {
|
|
29477
29518
|
let html = htmlCache.get("vnc-popout.html");
|
|
29478
29519
|
if (!html) {
|
|
29479
|
-
html =
|
|
29520
|
+
html = readFileSync43(resolve39(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
29480
29521
|
const name = escapeHtml(BRAND.productName);
|
|
29481
29522
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
29482
29523
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -29614,10 +29655,10 @@ var httpServer = serve({ fetch: app64.fetch, port, hostname });
|
|
|
29614
29655
|
console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
29615
29656
|
{
|
|
29616
29657
|
const reconcilePlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29617
|
-
const reconcileScript =
|
|
29658
|
+
const reconcileScript = resolve39(reconcilePlatformRoot, "plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js");
|
|
29618
29659
|
const RECONCILE_INTERVAL_MS = 12e4;
|
|
29619
29660
|
const runReconcile = (ctx) => {
|
|
29620
|
-
if (!
|
|
29661
|
+
if (!existsSync39(reconcileScript)) return;
|
|
29621
29662
|
try {
|
|
29622
29663
|
const child = spawn3(process.execPath, [reconcileScript], {
|
|
29623
29664
|
env: { ...process.env, PLATFORM_ROOT: reconcilePlatformRoot },
|
|
@@ -29640,10 +29681,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29640
29681
|
}
|
|
29641
29682
|
{
|
|
29642
29683
|
const outlookPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29643
|
-
const outlookScript =
|
|
29684
|
+
const outlookScript = resolve39(outlookPlatformRoot, "plugins/outlook/mcp/dist/scripts/complete-registration.js");
|
|
29644
29685
|
const OUTLOOK_COMPLETE_INTERVAL_MS = 3e4;
|
|
29645
29686
|
const runOutlookComplete = (ctx) => {
|
|
29646
|
-
if (!
|
|
29687
|
+
if (!existsSync39(outlookScript)) return;
|
|
29647
29688
|
try {
|
|
29648
29689
|
const child = spawn3(process.execPath, [outlookScript], {
|
|
29649
29690
|
env: { ...process.env, PLATFORM_ROOT: outlookPlatformRoot },
|
|
@@ -29666,18 +29707,18 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29666
29707
|
}
|
|
29667
29708
|
{
|
|
29668
29709
|
const auditRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29669
|
-
const strandedAccountsDir =
|
|
29710
|
+
const strandedAccountsDir = resolve39(auditRoot, "..", "data/accounts");
|
|
29670
29711
|
const STRANDED_AUDIT_INTERVAL_MS = 3e5;
|
|
29671
29712
|
const STRANDED_AGE_MS = 16 * 6e4;
|
|
29672
29713
|
const STRANDED_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
29673
29714
|
const runStrandedAudit = () => {
|
|
29674
29715
|
try {
|
|
29675
|
-
if (!
|
|
29716
|
+
if (!existsSync39(strandedAccountsDir)) return;
|
|
29676
29717
|
const now = Date.now();
|
|
29677
29718
|
for (const name of readdirSync26(strandedAccountsDir)) {
|
|
29678
29719
|
if (!STRANDED_UUID_RE.test(name)) continue;
|
|
29679
|
-
const pendingPath2 =
|
|
29680
|
-
if (!
|
|
29720
|
+
const pendingPath2 = resolve39(strandedAccountsDir, name, "secrets/outlook/pending-devicecode.enc");
|
|
29721
|
+
if (!existsSync39(pendingPath2)) continue;
|
|
29681
29722
|
const ageMs = now - statSync17(pendingPath2).mtimeMs;
|
|
29682
29723
|
if (ageMs > STRANDED_AGE_MS) {
|
|
29683
29724
|
console.error(`[outlook-mcp] devicecode-stranded account=${name} ageSec=${Math.floor(ageMs / 1e3)}`);
|
|
@@ -29696,7 +29737,7 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29696
29737
|
}
|
|
29697
29738
|
{
|
|
29698
29739
|
const googleRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29699
|
-
const googleAccountsDir =
|
|
29740
|
+
const googleAccountsDir = resolve39(googleRoot, "..", "data/accounts");
|
|
29700
29741
|
const GOOGLE_PENDING_AUDIT_INTERVAL_MS = 3e5;
|
|
29701
29742
|
const runGooglePendingAuditSafe = () => {
|
|
29702
29743
|
try {
|
|
@@ -29711,10 +29752,10 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29711
29752
|
firstRunDelayMs: 27e3,
|
|
29712
29753
|
run: runGooglePendingAuditSafe
|
|
29713
29754
|
});
|
|
29714
|
-
const googleAuditScript =
|
|
29755
|
+
const googleAuditScript = resolve39(googleRoot, "plugins/google/mcp/dist/scripts/account-audit.js");
|
|
29715
29756
|
const GOOGLE_ACCOUNT_AUDIT_INTERVAL_MS = 36e5;
|
|
29716
29757
|
const runGoogleAccountAudit = (ctx) => {
|
|
29717
|
-
if (!
|
|
29758
|
+
if (!existsSync39(googleAuditScript)) return;
|
|
29718
29759
|
try {
|
|
29719
29760
|
const child = spawn3(process.execPath, [googleAuditScript], {
|
|
29720
29761
|
env: { ...process.env, PLATFORM_ROOT: googleRoot },
|
|
@@ -29774,18 +29815,18 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29774
29815
|
}
|
|
29775
29816
|
{
|
|
29776
29817
|
const publishPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29777
|
-
const publishScript =
|
|
29818
|
+
const publishScript = resolve39(publishPlatformRoot, "plugins/scheduling/mcp/dist/scripts/publish-availability.js");
|
|
29778
29819
|
const PUBLISH_INTERVAL_MS = 3e5;
|
|
29779
|
-
const
|
|
29820
|
+
const bookingAccounts = () => {
|
|
29780
29821
|
try {
|
|
29781
|
-
return
|
|
29822
|
+
return listBookingSiteAccounts(listValidAccounts());
|
|
29782
29823
|
} catch (err) {
|
|
29783
|
-
console.error(`[calendar-availability] op=publish result=error reason=
|
|
29784
|
-
return
|
|
29824
|
+
console.error(`[calendar-availability] op=publish result=error reason=enumerate-failed err="${err.message}"`);
|
|
29825
|
+
return [];
|
|
29785
29826
|
}
|
|
29786
29827
|
};
|
|
29787
29828
|
const spawnPublish = (account, ctx) => {
|
|
29788
|
-
if (!
|
|
29829
|
+
if (!existsSync39(publishScript)) return;
|
|
29789
29830
|
try {
|
|
29790
29831
|
const child = spawn3(process.execPath, [publishScript], {
|
|
29791
29832
|
env: {
|
|
@@ -29804,21 +29845,21 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29804
29845
|
}
|
|
29805
29846
|
};
|
|
29806
29847
|
const auditSnapshotAge = (account) => {
|
|
29807
|
-
const availPath =
|
|
29848
|
+
const availPath = resolve39(account.accountDir, "calendar-availability.json");
|
|
29808
29849
|
let hasBookingSite = false;
|
|
29809
29850
|
try {
|
|
29810
|
-
if (
|
|
29811
|
-
const cfg = JSON.parse(
|
|
29851
|
+
if (existsSync39(availPath)) {
|
|
29852
|
+
const cfg = JSON.parse(readFileSync43(availPath, "utf-8"));
|
|
29812
29853
|
hasBookingSite = typeof cfg.bookingDbName === "string" && cfg.bookingDbName.length > 0;
|
|
29813
29854
|
}
|
|
29814
29855
|
} catch {
|
|
29815
29856
|
}
|
|
29816
29857
|
if (!hasBookingSite) return;
|
|
29817
|
-
const statePath =
|
|
29858
|
+
const statePath = resolve39(account.accountDir, "state", "booking-availability", "last-publish.json");
|
|
29818
29859
|
let lastSuccessAt = null;
|
|
29819
|
-
if (
|
|
29860
|
+
if (existsSync39(statePath)) {
|
|
29820
29861
|
try {
|
|
29821
|
-
const rec = JSON.parse(
|
|
29862
|
+
const rec = JSON.parse(readFileSync43(statePath, "utf-8"));
|
|
29822
29863
|
lastSuccessAt = rec.lastSuccessAt ?? null;
|
|
29823
29864
|
} catch {
|
|
29824
29865
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=na reason=bad-state-file`);
|
|
@@ -29830,10 +29871,14 @@ console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
|
|
|
29830
29871
|
console.error(`[calendar-availability] op=audit accountId=${account.accountId} snapshotAgeMs=${ageMs ?? "na"}`);
|
|
29831
29872
|
};
|
|
29832
29873
|
const publishTick = (ctx) => {
|
|
29833
|
-
const account
|
|
29834
|
-
|
|
29835
|
-
|
|
29836
|
-
|
|
29874
|
+
for (const account of bookingAccounts()) {
|
|
29875
|
+
try {
|
|
29876
|
+
spawnPublish(account, ctx);
|
|
29877
|
+
auditSnapshotAge(account);
|
|
29878
|
+
} catch (err) {
|
|
29879
|
+
console.error(`[calendar-availability] op=publish accountId=${account.accountId} result=error reason=tick-failed err="${err.message}"`);
|
|
29880
|
+
}
|
|
29881
|
+
}
|
|
29837
29882
|
};
|
|
29838
29883
|
registerLoop({
|
|
29839
29884
|
name: "calendar-availability-publish",
|
|
@@ -29847,11 +29892,11 @@ startTimeEntryCensus(() => getSession());
|
|
|
29847
29892
|
startLedgerCensus(() => getSession());
|
|
29848
29893
|
{
|
|
29849
29894
|
const auditPlatformRoot = process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..");
|
|
29850
|
-
const auditScript =
|
|
29895
|
+
const auditScript = resolve39(auditPlatformRoot, "plugins/connector/mcp/dist/scripts/audit-connectors.js");
|
|
29851
29896
|
const auditLogDir = process.env.LOG_DIR ?? LOG_DIR;
|
|
29852
29897
|
const CONNECTOR_AUDIT_INTERVAL_MS = 3e5;
|
|
29853
29898
|
const runConnectorAudit = (ctx) => {
|
|
29854
|
-
if (!
|
|
29899
|
+
if (!existsSync39(auditScript)) return;
|
|
29855
29900
|
try {
|
|
29856
29901
|
const child = spawn3(process.execPath, [auditScript], {
|
|
29857
29902
|
env: { ...process.env, PLATFORM_ROOT: auditPlatformRoot, LOG_DIR: auditLogDir },
|
|
@@ -29971,11 +30016,11 @@ try {
|
|
|
29971
30016
|
var ADMINUSER_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
29972
30017
|
async function runAdminUserReconcileTick() {
|
|
29973
30018
|
try {
|
|
29974
|
-
if (!
|
|
30019
|
+
if (!existsSync39(USERS_FILE)) {
|
|
29975
30020
|
console.error("[adminuser-self-heal] skip reason=no-users-file");
|
|
29976
30021
|
return;
|
|
29977
30022
|
}
|
|
29978
|
-
const usersRaw =
|
|
30023
|
+
const usersRaw = readFileSync43(USERS_FILE, "utf-8").trim();
|
|
29979
30024
|
if (!usersRaw) {
|
|
29980
30025
|
console.error("[adminuser-self-heal] skip reason=empty-users-file");
|
|
29981
30026
|
return;
|
|
@@ -30022,8 +30067,8 @@ registerLoop({
|
|
|
30022
30067
|
});
|
|
30023
30068
|
var USERS_RECONCILE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
30024
30069
|
function countUsersRows() {
|
|
30025
|
-
if (!
|
|
30026
|
-
const raw =
|
|
30070
|
+
if (!existsSync39(USERS_FILE)) return 0;
|
|
30071
|
+
const raw = readFileSync43(USERS_FILE, "utf-8").trim();
|
|
30027
30072
|
if (!raw) return 0;
|
|
30028
30073
|
const users = JSON.parse(raw);
|
|
30029
30074
|
return users.filter((u) => typeof u.userId === "string").length;
|
|
@@ -30156,7 +30201,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
30156
30201
|
}
|
|
30157
30202
|
init({
|
|
30158
30203
|
configDir: configDirForWhatsApp,
|
|
30159
|
-
platformRoot:
|
|
30204
|
+
platformRoot: resolve39(process.env.MAXY_PLATFORM_ROOT ?? join45(__dirname, "..")),
|
|
30160
30205
|
accountConfig: bootAccountConfig,
|
|
30161
30206
|
onMessage: async (msg) => {
|
|
30162
30207
|
if (isObserveAccount(msg.accountId)) {
|