@open-mercato/cli 0.6.8-develop.6910.1.560e304de2 → 0.6.8-develop.6912.1.36161dc9ac
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/dist/agentic/guides/module-facts.json +110 -110
- package/dist/agentic/guides/modules/ai_assistant.md +1 -1
- package/dist/agentic/guides/modules/api_docs.md +1 -1
- package/dist/agentic/guides/modules/api_keys.md +1 -1
- package/dist/agentic/guides/modules/attachments.md +1 -1
- package/dist/agentic/guides/modules/audit_logs.md +1 -1
- package/dist/agentic/guides/modules/auth.md +1 -1
- package/dist/agentic/guides/modules/business_rules.md +1 -1
- package/dist/agentic/guides/modules/catalog.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail.md +1 -1
- package/dist/agentic/guides/modules/channel_imap.md +1 -1
- package/dist/agentic/guides/modules/checkout.md +1 -1
- package/dist/agentic/guides/modules/communication_channels.md +1 -1
- package/dist/agentic/guides/modules/configs.md +1 -1
- package/dist/agentic/guides/modules/content.md +1 -1
- package/dist/agentic/guides/modules/currencies.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts.md +1 -1
- package/dist/agentic/guides/modules/customers.md +1 -1
- package/dist/agentic/guides/modules/dashboards.md +1 -1
- package/dist/agentic/guides/modules/data_sync.md +1 -1
- package/dist/agentic/guides/modules/design_system.md +1 -1
- package/dist/agentic/guides/modules/dictionaries.md +1 -1
- package/dist/agentic/guides/modules/directory.md +1 -1
- package/dist/agentic/guides/modules/entities.md +1 -1
- package/dist/agentic/guides/modules/events.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe.md +1 -1
- package/dist/agentic/guides/modules/generators.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops.md +1 -1
- package/dist/agentic/guides/modules/integrations.md +1 -1
- package/dist/agentic/guides/modules/messages.md +1 -1
- package/dist/agentic/guides/modules/notifications.md +1 -1
- package/dist/agentic/guides/modules/onboarding.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways.md +1 -1
- package/dist/agentic/guides/modules/perspectives.md +1 -1
- package/dist/agentic/guides/modules/planner.md +1 -1
- package/dist/agentic/guides/modules/portal.md +1 -1
- package/dist/agentic/guides/modules/progress.md +1 -1
- package/dist/agentic/guides/modules/query_index.md +1 -1
- package/dist/agentic/guides/modules/record_locks.md +1 -1
- package/dist/agentic/guides/modules/resources.md +1 -1
- package/dist/agentic/guides/modules/sales.md +1 -1
- package/dist/agentic/guides/modules/scheduler.md +1 -1
- package/dist/agentic/guides/modules/search.md +1 -1
- package/dist/agentic/guides/modules/security.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers.md +1 -1
- package/dist/agentic/guides/modules/sso.md +1 -1
- package/dist/agentic/guides/modules/staff.md +1 -1
- package/dist/agentic/guides/modules/storage_s3.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo.md +1 -1
- package/dist/agentic/guides/modules/sync_excel.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays.md +1 -1
- package/dist/agentic/guides/modules/translations.md +1 -1
- package/dist/agentic/guides/modules/webhooks.md +1 -1
- package/dist/agentic/guides/modules/wms.md +1 -1
- package/dist/agentic/guides/modules/workflows.md +1 -1
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +2 -2
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +1 -1
- package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +11 -1
- package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/export-codex-session.mjs +213 -0
- package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +24 -3
- package/dist/mercato.js +3 -2
- package/dist/mercato.js.map +2 -2
- package/jest.config.cjs +1 -1
- package/package.json +6 -6
- package/src/mercato.ts +2 -1
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'node:child_process'
|
|
4
|
+
import {
|
|
5
|
+
closeSync,
|
|
6
|
+
existsSync,
|
|
7
|
+
lstatSync,
|
|
8
|
+
openSync,
|
|
9
|
+
realpathSync,
|
|
10
|
+
unlinkSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from 'node:fs'
|
|
13
|
+
import { basename, dirname, resolve } from 'node:path'
|
|
14
|
+
import { pathToFileURL } from 'node:url'
|
|
15
|
+
|
|
16
|
+
const MAX_PROTOCOL_BYTES = 25 * 1024 * 1024
|
|
17
|
+
const MAX_EXPORT_BYTES = 20 * 1024 * 1024
|
|
18
|
+
const THREAD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
19
|
+
|
|
20
|
+
function fail(message) {
|
|
21
|
+
throw new Error(message)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArguments(argumentsList) {
|
|
25
|
+
const allowed = new Set(['thread-id', 'out'])
|
|
26
|
+
const values = new Map()
|
|
27
|
+
for (let index = 0; index < argumentsList.length; index += 1) {
|
|
28
|
+
const argument = argumentsList[index]
|
|
29
|
+
if (!argument.startsWith('--')) fail('Every argument must use a named --flag.')
|
|
30
|
+
const key = argument.slice(2)
|
|
31
|
+
if (!allowed.has(key)) fail(`Unknown argument: --${key}`)
|
|
32
|
+
if (values.has(key)) fail(`Duplicate argument: --${key}`)
|
|
33
|
+
const value = argumentsList[index + 1]
|
|
34
|
+
if (!value || value.startsWith('--')) fail(`Missing value for --${key}.`)
|
|
35
|
+
values.set(key, value)
|
|
36
|
+
index += 1
|
|
37
|
+
}
|
|
38
|
+
for (const required of allowed) {
|
|
39
|
+
if (!values.has(required)) fail(`Missing required argument: --${required}.`)
|
|
40
|
+
}
|
|
41
|
+
return Object.fromEntries(values)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateThread(thread, expectedThreadId) {
|
|
45
|
+
if (!thread || typeof thread !== 'object' || Array.isArray(thread)) {
|
|
46
|
+
fail('Codex app-server returned no thread object.')
|
|
47
|
+
}
|
|
48
|
+
const returnedIds = [thread.id, thread.sessionId].filter((value) => typeof value === 'string')
|
|
49
|
+
if (!returnedIds.includes(expectedThreadId)) {
|
|
50
|
+
fail('Codex app-server returned a different thread than requested.')
|
|
51
|
+
}
|
|
52
|
+
if (!Array.isArray(thread.turns) || thread.turns.length === 0) {
|
|
53
|
+
fail('Codex app-server returned a thread without turns.')
|
|
54
|
+
}
|
|
55
|
+
return thread
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readThreadFromAppServer(threadId, timeoutMilliseconds = 30_000) {
|
|
59
|
+
return new Promise((resolveThread, rejectThread) => {
|
|
60
|
+
const server = spawn('codex', ['app-server', '--stdio'], {
|
|
61
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
62
|
+
})
|
|
63
|
+
let settled = false
|
|
64
|
+
let initialized = false
|
|
65
|
+
let protocolBytes = 0
|
|
66
|
+
let stdoutBuffer = ''
|
|
67
|
+
|
|
68
|
+
const settle = (error, thread) => {
|
|
69
|
+
if (settled) return
|
|
70
|
+
settled = true
|
|
71
|
+
clearTimeout(timeout)
|
|
72
|
+
server.stdin.end()
|
|
73
|
+
if (server.exitCode === null && !server.killed) server.kill()
|
|
74
|
+
if (error) rejectThread(error)
|
|
75
|
+
else resolveThread(thread)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const send = (message) => {
|
|
79
|
+
if (!server.stdin.destroyed) server.stdin.write(`${JSON.stringify(message)}\n`)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const handleMessage = (line) => {
|
|
83
|
+
if (!line.trim()) return
|
|
84
|
+
let message
|
|
85
|
+
try {
|
|
86
|
+
message = JSON.parse(line)
|
|
87
|
+
} catch {
|
|
88
|
+
settle(new Error('Codex app-server returned malformed JSON.'))
|
|
89
|
+
return
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (message.id === 1) {
|
|
93
|
+
if (message.error || !message.result || initialized) {
|
|
94
|
+
settle(new Error('Codex app-server initialization failed.'))
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
initialized = true
|
|
98
|
+
send({ method: 'initialized', params: {} })
|
|
99
|
+
send({ id: 2, method: 'thread/read', params: { threadId, includeTurns: true } })
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (message.id === 2) {
|
|
104
|
+
if (message.error) {
|
|
105
|
+
settle(new Error('Codex app-server could not read the requested thread.'))
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
settle(null, validateThread(message.result?.thread, threadId))
|
|
110
|
+
} catch (error) {
|
|
111
|
+
settle(error)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const timeout = setTimeout(() => {
|
|
117
|
+
settle(new Error('Timed out while reading the Codex thread.'))
|
|
118
|
+
}, timeoutMilliseconds)
|
|
119
|
+
|
|
120
|
+
server.on('error', () => {
|
|
121
|
+
settle(new Error('Could not start the Codex app-server.'))
|
|
122
|
+
})
|
|
123
|
+
server.stdin.on('error', () => {
|
|
124
|
+
if (!settled) settle(new Error('Codex app-server closed its input before returning the thread.'))
|
|
125
|
+
})
|
|
126
|
+
server.on('close', () => {
|
|
127
|
+
if (!settled) settle(new Error('Codex app-server exited before returning the thread.'))
|
|
128
|
+
})
|
|
129
|
+
server.stdout.setEncoding('utf8')
|
|
130
|
+
server.stdout.on('data', (chunk) => {
|
|
131
|
+
protocolBytes += Buffer.byteLength(chunk)
|
|
132
|
+
if (protocolBytes > MAX_PROTOCOL_BYTES) {
|
|
133
|
+
settle(new Error('Codex app-server response exceeds the allowed size.'))
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
stdoutBuffer += chunk
|
|
137
|
+
while (stdoutBuffer.includes('\n')) {
|
|
138
|
+
const newlineIndex = stdoutBuffer.indexOf('\n')
|
|
139
|
+
const line = stdoutBuffer.slice(0, newlineIndex)
|
|
140
|
+
stdoutBuffer = stdoutBuffer.slice(newlineIndex + 1)
|
|
141
|
+
handleMessage(line)
|
|
142
|
+
if (settled) return
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
send({
|
|
147
|
+
id: 1,
|
|
148
|
+
method: 'initialize',
|
|
149
|
+
params: {
|
|
150
|
+
clientInfo: { name: 'om-share-this-session', version: '1.0.0' },
|
|
151
|
+
capabilities: { experimentalApi: true },
|
|
152
|
+
},
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function writeExport(outputPath, thread) {
|
|
158
|
+
const serialized = `${JSON.stringify(thread, null, 2)}\n`
|
|
159
|
+
if (Buffer.byteLength(serialized) > MAX_EXPORT_BYTES) {
|
|
160
|
+
fail('Codex thread export exceeds the allowed size.')
|
|
161
|
+
}
|
|
162
|
+
const descriptor = openSync(outputPath, 'wx', 0o600)
|
|
163
|
+
let complete = false
|
|
164
|
+
try {
|
|
165
|
+
writeFileSync(descriptor, serialized, 'utf8')
|
|
166
|
+
complete = true
|
|
167
|
+
} finally {
|
|
168
|
+
closeSync(descriptor)
|
|
169
|
+
if (!complete && existsSync(outputPath)) unlinkSync(outputPath)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export async function exportCodexSession({ threadId, outputPath }) {
|
|
174
|
+
if (!THREAD_ID_PATTERN.test(threadId)) fail('Codex thread ID must be a UUID.')
|
|
175
|
+
const resolvedOutputPath = resolve(outputPath)
|
|
176
|
+
if (basename(resolvedOutputPath) === '.' || existsSync(resolvedOutputPath)) {
|
|
177
|
+
fail('Output path already exists; use a new temporary file.')
|
|
178
|
+
}
|
|
179
|
+
const outputParent = dirname(resolvedOutputPath)
|
|
180
|
+
let parentStat
|
|
181
|
+
try {
|
|
182
|
+
parentStat = lstatSync(outputParent)
|
|
183
|
+
realpathSync(outputParent)
|
|
184
|
+
} catch {
|
|
185
|
+
fail('Output parent must be an existing real directory.')
|
|
186
|
+
}
|
|
187
|
+
if (!parentStat.isDirectory() || parentStat.isSymbolicLink()) {
|
|
188
|
+
fail('Output parent must be an existing real directory.')
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const thread = await readThreadFromAppServer(threadId)
|
|
192
|
+
writeExport(resolvedOutputPath, thread)
|
|
193
|
+
return { turns: thread.turns.length }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function main() {
|
|
197
|
+
const argumentsMap = parseArguments(process.argv.slice(2))
|
|
198
|
+
const result = await exportCodexSession({
|
|
199
|
+
threadId: argumentsMap['thread-id'],
|
|
200
|
+
outputPath: argumentsMap.out,
|
|
201
|
+
})
|
|
202
|
+
process.stdout.write(`${JSON.stringify({ status: 'exported', turns: result.turns })}\n`)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
206
|
+
try {
|
|
207
|
+
await main()
|
|
208
|
+
} catch (error) {
|
|
209
|
+
const message = error instanceof Error ? error.message : 'Unknown Codex session-export failure.'
|
|
210
|
+
process.stderr.write(`Codex session was not exported: ${message}\n`)
|
|
211
|
+
process.exitCode = 1
|
|
212
|
+
}
|
|
213
|
+
}
|
package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs
CHANGED
|
@@ -161,14 +161,17 @@ function sanitizeString(input, key, state) {
|
|
|
161
161
|
value = replaceMatches(value, /\b(Authorization\s*:\s*)[^\s,;]+/gi, (_match, prefix) => `${prefix}«redacted:authorization»`, state, 'secrets')
|
|
162
162
|
value = replaceMatches(value, /([a-z][a-z0-9+.-]*:\/\/)[^/\s:@]+:[^/\s@]+@/gi, (_match, prefix) => `${prefix}«redacted:credential»@`, state, 'secrets')
|
|
163
163
|
value = replaceMatches(value, /\b((?:api[_-]?key|access[_-]?token|refresh[_-]?token|password|passwd|secret|client[_-]?secret)\s*[:=]\s*)[^\s,;"']+/gi, (_match, prefix) => `${prefix}«redacted:secret»`, state, 'secrets')
|
|
164
|
-
value = redactHighEntropy(value, state)
|
|
164
|
+
if (key !== 'generated-file-path') value = redactHighEntropy(value, state)
|
|
165
165
|
|
|
166
166
|
value = replaceMatches(value, /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, '«redacted:email»', state, 'pii')
|
|
167
167
|
value = replaceMatches(value, /\b(?:\d{1,3}\.){3}\d{1,3}\b/g, '«redacted:ip»', state, 'pii')
|
|
168
168
|
value = replaceMatches(value, /\b(?:[A-F0-9]{1,4}:){2,7}[A-F0-9]{1,4}\b/gi, '«redacted:ip»', state, 'pii')
|
|
169
169
|
value = replaceMatches(value, /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi, '«redacted:identifier»', state, 'identifiers')
|
|
170
170
|
value = replaceMatches(value, /\b[0-9a-f]{64,}\b/gi, '«redacted:hex-secret»', state, 'secrets')
|
|
171
|
-
|
|
171
|
+
const phonePattern = key === 'generated-file-path'
|
|
172
|
+
? /(?<![A-Za-z0-9_])\+?\d[\d ()-]{7,}\d(?![A-Za-z0-9_])/g
|
|
173
|
+
: /\+?\d[\d ()-]{7,}\d/g
|
|
174
|
+
value = value.replace(phonePattern, (candidate) => {
|
|
172
175
|
const digits = candidate.replace(/\D/g, '')
|
|
173
176
|
if (digits.length < 9 || digits.length > 15) return candidate
|
|
174
177
|
countReplacement(state, 'pii')
|
|
@@ -183,6 +186,10 @@ function sanitizeNode(value, key, state) {
|
|
|
183
186
|
if (Array.isArray(value)) return value.map((item) => sanitizeNode(item, '', state))
|
|
184
187
|
if (!value || typeof value !== 'object') return value
|
|
185
188
|
|
|
189
|
+
const redactsBrowserTabListing = value.type === 'mcpToolCall'
|
|
190
|
+
&& typeof value.arguments?.code === 'string'
|
|
191
|
+
&& /\.user\.openTabs\s*\(/.test(value.arguments.code)
|
|
192
|
+
|
|
186
193
|
for (const [candidateKey, candidateValue] of Object.entries(value)) {
|
|
187
194
|
if (pathKeyPattern.test(candidateKey) && typeof candidateValue === 'string' && isDangerousPath(candidateValue)) {
|
|
188
195
|
countReplacement(state, 'dangerous')
|
|
@@ -194,6 +201,11 @@ function sanitizeNode(value, key, state) {
|
|
|
194
201
|
for (const [childKey, childValue] of Object.entries(value)) {
|
|
195
202
|
const sanitizedKey = sanitizeString(childKey, 'object-key', state)
|
|
196
203
|
if (Object.hasOwn(output, sanitizedKey)) fail('Sanitization produced duplicate object keys.')
|
|
204
|
+
if (redactsBrowserTabListing && childKey === 'result') {
|
|
205
|
+
countReplacement(state, 'pii')
|
|
206
|
+
output[sanitizedKey] = { redacted: 'browser-tab-list' }
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
197
209
|
output[sanitizedKey] = sanitizeNode(childValue, childKey, state)
|
|
198
210
|
}
|
|
199
211
|
return output
|
|
@@ -203,10 +215,19 @@ function inferRole(value) {
|
|
|
203
215
|
if (!value || typeof value !== 'object') return null
|
|
204
216
|
for (const candidate of [value.type, value.role, value.message?.role]) {
|
|
205
217
|
if (candidate === 'user' || candidate === 'assistant') return candidate
|
|
218
|
+
if (candidate === 'userMessage') return 'user'
|
|
219
|
+
if (candidate === 'agentMessage') return 'assistant'
|
|
206
220
|
}
|
|
207
221
|
return null
|
|
208
222
|
}
|
|
209
223
|
|
|
224
|
+
function inferRoles(value) {
|
|
225
|
+
const directRole = inferRole(value)
|
|
226
|
+
if (directRole) return [directRole]
|
|
227
|
+
if (!value || typeof value !== 'object' || !Array.isArray(value.items)) return []
|
|
228
|
+
return [...new Set(value.items.map(inferRole).filter(Boolean))]
|
|
229
|
+
}
|
|
230
|
+
|
|
210
231
|
function analyzeSession(session) {
|
|
211
232
|
let entries
|
|
212
233
|
let collectionName
|
|
@@ -224,7 +245,7 @@ function analyzeSession(session) {
|
|
|
224
245
|
}
|
|
225
246
|
if (!entries) fail('Session JSON has no recognizable turn/event collection.')
|
|
226
247
|
|
|
227
|
-
const roles = entries.
|
|
248
|
+
const roles = entries.flatMap(inferRoles)
|
|
228
249
|
const userTurns = roles.filter((role) => role === 'user').length
|
|
229
250
|
const assistantTurns = roles.filter((role) => role === 'assistant').length
|
|
230
251
|
if (userTurns === 0 || assistantTurns === 0) {
|
package/dist/mercato.js
CHANGED
|
@@ -2,7 +2,7 @@ import { registerWorkerShutdownHook, runWorker } from "@open-mercato/queue/worke
|
|
|
2
2
|
import { getCliModules, hasCliModules, registerCliModules } from "./registry.js";
|
|
3
3
|
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
4
4
|
import { getSslConfig } from "@open-mercato/shared/lib/db/ssl";
|
|
5
|
-
import { getRedisUrl, getRedisUrlOrThrow } from "@open-mercato/shared/lib/redis/connection";
|
|
5
|
+
import { getRedisUrl, getRedisUrlOrThrow, REDIS_WIRE_PROTOCOL } from "@open-mercato/shared/lib/redis/connection";
|
|
6
6
|
import { resolveInitDerivedSecrets } from "./lib/init-secrets.js";
|
|
7
7
|
import {
|
|
8
8
|
resolveAutoSpawnWorkersMode,
|
|
@@ -764,7 +764,8 @@ async function run(argv = process.argv) {
|
|
|
764
764
|
connectTimeout: 3e3,
|
|
765
765
|
maxRetriesPerRequest: 1,
|
|
766
766
|
retryStrategy: () => null,
|
|
767
|
-
enableOfflineQueue: false
|
|
767
|
+
enableOfflineQueue: false,
|
|
768
|
+
protocol: REDIS_WIRE_PROTOCOL
|
|
768
769
|
});
|
|
769
770
|
redis.on("error", () => {
|
|
770
771
|
});
|