@dedenlabs/claude-code-router-cli 2.0.7 → 2.0.8-beta.1
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 +19 -2
- package/dist/cli.js +15630 -3236
- package/package.json +7 -2
- package/dist/lib/pino.js +0 -234
- package/dist/lib/worker.js +0 -194
- package/dist/reproduce_issue.js +0 -2027
- package/dist/standalone.js +0 -81387
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dedenlabs/claude-code-router-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8-beta.1",
|
|
4
4
|
"description": "基于@musistudio/claude-code-router的增强版CLI路由工具 - 支持统一路由引擎、外部规则加载、智能日志系统、自动配置迁移和GLM思考模式",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ccr": "dist/cli.js"
|
|
@@ -38,17 +38,22 @@
|
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/dedenlabs/claude-code-router-cli#readme",
|
|
40
40
|
"dependencies": {
|
|
41
|
+
"@anthropic-ai/sdk": "^0.54.0",
|
|
42
|
+
"@fastify/cors": "^11.0.1",
|
|
41
43
|
"@fastify/static": "^8.2.0",
|
|
44
|
+
"@google/genai": "^1.7.0",
|
|
42
45
|
"@inquirer/prompts": "^5.0.0",
|
|
43
|
-
"@musistudio/llms": "^1.0.
|
|
46
|
+
"@musistudio/llms": "^1.0.51",
|
|
44
47
|
"dotenv": "^16.4.7",
|
|
45
48
|
"find-process": "^2.0.0",
|
|
46
49
|
"json5": "^2.2.3",
|
|
47
50
|
"lru-cache": "^11.2.2",
|
|
48
51
|
"minimist": "^1.2.8",
|
|
52
|
+
"openai": "^5.6.0",
|
|
49
53
|
"rotating-file-stream": "^3.2.7",
|
|
50
54
|
"shell-quote": "^1.8.3",
|
|
51
55
|
"tiktoken": "^1.0.21",
|
|
56
|
+
"undici": "^7.10.0",
|
|
52
57
|
"uuid": "^11.1.0"
|
|
53
58
|
},
|
|
54
59
|
"devDependencies": {
|
package/dist/lib/pino.js
DELETED
|
@@ -1,234 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const os = require('node:os')
|
|
4
|
-
const stdSerializers = require('pino-std-serializers')
|
|
5
|
-
const caller = require('./lib/caller')
|
|
6
|
-
const redaction = require('./lib/redaction')
|
|
7
|
-
const time = require('./lib/time')
|
|
8
|
-
const proto = require('./lib/proto')
|
|
9
|
-
const symbols = require('./lib/symbols')
|
|
10
|
-
const { configure } = require('safe-stable-stringify')
|
|
11
|
-
const { assertDefaultLevelFound, mappings, genLsCache, genLevelComparison, assertLevelComparison } = require('./lib/levels')
|
|
12
|
-
const { DEFAULT_LEVELS, SORTING_ORDER } = require('./lib/constants')
|
|
13
|
-
const {
|
|
14
|
-
createArgsNormalizer,
|
|
15
|
-
asChindings,
|
|
16
|
-
buildSafeSonicBoom,
|
|
17
|
-
buildFormatters,
|
|
18
|
-
stringify,
|
|
19
|
-
normalizeDestFileDescriptor,
|
|
20
|
-
noop
|
|
21
|
-
} = require('./lib/tools')
|
|
22
|
-
const { version } = require('./lib/meta')
|
|
23
|
-
const {
|
|
24
|
-
chindingsSym,
|
|
25
|
-
redactFmtSym,
|
|
26
|
-
serializersSym,
|
|
27
|
-
timeSym,
|
|
28
|
-
timeSliceIndexSym,
|
|
29
|
-
streamSym,
|
|
30
|
-
stringifySym,
|
|
31
|
-
stringifySafeSym,
|
|
32
|
-
stringifiersSym,
|
|
33
|
-
setLevelSym,
|
|
34
|
-
endSym,
|
|
35
|
-
formatOptsSym,
|
|
36
|
-
messageKeySym,
|
|
37
|
-
errorKeySym,
|
|
38
|
-
nestedKeySym,
|
|
39
|
-
mixinSym,
|
|
40
|
-
levelCompSym,
|
|
41
|
-
useOnlyCustomLevelsSym,
|
|
42
|
-
formattersSym,
|
|
43
|
-
hooksSym,
|
|
44
|
-
nestedKeyStrSym,
|
|
45
|
-
mixinMergeStrategySym,
|
|
46
|
-
msgPrefixSym
|
|
47
|
-
} = symbols
|
|
48
|
-
const { epochTime, nullTime } = time
|
|
49
|
-
const { pid } = process
|
|
50
|
-
const hostname = os.hostname()
|
|
51
|
-
const defaultErrorSerializer = stdSerializers.err
|
|
52
|
-
const defaultOptions = {
|
|
53
|
-
level: 'info',
|
|
54
|
-
levelComparison: SORTING_ORDER.ASC,
|
|
55
|
-
levels: DEFAULT_LEVELS,
|
|
56
|
-
messageKey: 'msg',
|
|
57
|
-
errorKey: 'err',
|
|
58
|
-
nestedKey: null,
|
|
59
|
-
enabled: true,
|
|
60
|
-
base: { pid, hostname },
|
|
61
|
-
serializers: Object.assign(Object.create(null), {
|
|
62
|
-
err: defaultErrorSerializer
|
|
63
|
-
}),
|
|
64
|
-
formatters: Object.assign(Object.create(null), {
|
|
65
|
-
bindings (bindings) {
|
|
66
|
-
return bindings
|
|
67
|
-
},
|
|
68
|
-
level (label, number) {
|
|
69
|
-
return { level: number }
|
|
70
|
-
}
|
|
71
|
-
}),
|
|
72
|
-
hooks: {
|
|
73
|
-
logMethod: undefined,
|
|
74
|
-
streamWrite: undefined
|
|
75
|
-
},
|
|
76
|
-
timestamp: epochTime,
|
|
77
|
-
name: undefined,
|
|
78
|
-
redact: null,
|
|
79
|
-
customLevels: null,
|
|
80
|
-
useOnlyCustomLevels: false,
|
|
81
|
-
depthLimit: 5,
|
|
82
|
-
edgeLimit: 100
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const normalize = createArgsNormalizer(defaultOptions)
|
|
86
|
-
|
|
87
|
-
const serializers = Object.assign(Object.create(null), stdSerializers)
|
|
88
|
-
|
|
89
|
-
function pino (...args) {
|
|
90
|
-
const instance = {}
|
|
91
|
-
const { opts, stream } = normalize(instance, caller(), ...args)
|
|
92
|
-
|
|
93
|
-
if (opts.level && typeof opts.level === 'string' && DEFAULT_LEVELS[opts.level.toLowerCase()] !== undefined) opts.level = opts.level.toLowerCase()
|
|
94
|
-
|
|
95
|
-
const {
|
|
96
|
-
redact,
|
|
97
|
-
crlf,
|
|
98
|
-
serializers,
|
|
99
|
-
timestamp,
|
|
100
|
-
messageKey,
|
|
101
|
-
errorKey,
|
|
102
|
-
nestedKey,
|
|
103
|
-
base,
|
|
104
|
-
name,
|
|
105
|
-
level,
|
|
106
|
-
customLevels,
|
|
107
|
-
levelComparison,
|
|
108
|
-
mixin,
|
|
109
|
-
mixinMergeStrategy,
|
|
110
|
-
useOnlyCustomLevels,
|
|
111
|
-
formatters,
|
|
112
|
-
hooks,
|
|
113
|
-
depthLimit,
|
|
114
|
-
edgeLimit,
|
|
115
|
-
onChild,
|
|
116
|
-
msgPrefix
|
|
117
|
-
} = opts
|
|
118
|
-
|
|
119
|
-
const stringifySafe = configure({
|
|
120
|
-
maximumDepth: depthLimit,
|
|
121
|
-
maximumBreadth: edgeLimit
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
const allFormatters = buildFormatters(
|
|
125
|
-
formatters.level,
|
|
126
|
-
formatters.bindings,
|
|
127
|
-
formatters.log
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
const stringifyFn = stringify.bind({
|
|
131
|
-
[stringifySafeSym]: stringifySafe
|
|
132
|
-
})
|
|
133
|
-
const stringifiers = redact ? redaction(redact, stringifyFn) : {}
|
|
134
|
-
const formatOpts = redact
|
|
135
|
-
? { stringify: stringifiers[redactFmtSym] }
|
|
136
|
-
: { stringify: stringifyFn }
|
|
137
|
-
const end = '}' + (crlf ? '\r\n' : '\n')
|
|
138
|
-
const coreChindings = asChindings.bind(null, {
|
|
139
|
-
[chindingsSym]: '',
|
|
140
|
-
[serializersSym]: serializers,
|
|
141
|
-
[stringifiersSym]: stringifiers,
|
|
142
|
-
[stringifySym]: stringify,
|
|
143
|
-
[stringifySafeSym]: stringifySafe,
|
|
144
|
-
[formattersSym]: allFormatters
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
let chindings = ''
|
|
148
|
-
if (base !== null) {
|
|
149
|
-
if (name === undefined) {
|
|
150
|
-
chindings = coreChindings(base)
|
|
151
|
-
} else {
|
|
152
|
-
chindings = coreChindings(Object.assign({}, base, { name }))
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const time = (timestamp instanceof Function)
|
|
157
|
-
? timestamp
|
|
158
|
-
: (timestamp ? epochTime : nullTime)
|
|
159
|
-
const timeSliceIndex = time().indexOf(':') + 1
|
|
160
|
-
|
|
161
|
-
if (useOnlyCustomLevels && !customLevels) throw Error('customLevels is required if useOnlyCustomLevels is set true')
|
|
162
|
-
if (mixin && typeof mixin !== 'function') throw Error(`Unknown mixin type "${typeof mixin}" - expected "function"`)
|
|
163
|
-
if (msgPrefix && typeof msgPrefix !== 'string') throw Error(`Unknown msgPrefix type "${typeof msgPrefix}" - expected "string"`)
|
|
164
|
-
|
|
165
|
-
assertDefaultLevelFound(level, customLevels, useOnlyCustomLevels)
|
|
166
|
-
const levels = mappings(customLevels, useOnlyCustomLevels)
|
|
167
|
-
|
|
168
|
-
if (typeof stream.emit === 'function') {
|
|
169
|
-
stream.emit('message', { code: 'PINO_CONFIG', config: { levels, messageKey, errorKey } })
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
assertLevelComparison(levelComparison)
|
|
173
|
-
const levelCompFunc = genLevelComparison(levelComparison)
|
|
174
|
-
|
|
175
|
-
Object.assign(instance, {
|
|
176
|
-
levels,
|
|
177
|
-
[levelCompSym]: levelCompFunc,
|
|
178
|
-
[useOnlyCustomLevelsSym]: useOnlyCustomLevels,
|
|
179
|
-
[streamSym]: stream,
|
|
180
|
-
[timeSym]: time,
|
|
181
|
-
[timeSliceIndexSym]: timeSliceIndex,
|
|
182
|
-
[stringifySym]: stringify,
|
|
183
|
-
[stringifySafeSym]: stringifySafe,
|
|
184
|
-
[stringifiersSym]: stringifiers,
|
|
185
|
-
[endSym]: end,
|
|
186
|
-
[formatOptsSym]: formatOpts,
|
|
187
|
-
[messageKeySym]: messageKey,
|
|
188
|
-
[errorKeySym]: errorKey,
|
|
189
|
-
[nestedKeySym]: nestedKey,
|
|
190
|
-
// protect against injection
|
|
191
|
-
[nestedKeyStrSym]: nestedKey ? `,${JSON.stringify(nestedKey)}:{` : '',
|
|
192
|
-
[serializersSym]: serializers,
|
|
193
|
-
[mixinSym]: mixin,
|
|
194
|
-
[mixinMergeStrategySym]: mixinMergeStrategy,
|
|
195
|
-
[chindingsSym]: chindings,
|
|
196
|
-
[formattersSym]: allFormatters,
|
|
197
|
-
[hooksSym]: hooks,
|
|
198
|
-
silent: noop,
|
|
199
|
-
onChild,
|
|
200
|
-
[msgPrefixSym]: msgPrefix
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
Object.setPrototypeOf(instance, proto())
|
|
204
|
-
|
|
205
|
-
genLsCache(instance)
|
|
206
|
-
|
|
207
|
-
instance[setLevelSym](level)
|
|
208
|
-
|
|
209
|
-
return instance
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
module.exports = pino
|
|
213
|
-
|
|
214
|
-
module.exports.destination = (dest = process.stdout.fd) => {
|
|
215
|
-
if (typeof dest === 'object') {
|
|
216
|
-
dest.dest = normalizeDestFileDescriptor(dest.dest || process.stdout.fd)
|
|
217
|
-
return buildSafeSonicBoom(dest)
|
|
218
|
-
} else {
|
|
219
|
-
return buildSafeSonicBoom({ dest: normalizeDestFileDescriptor(dest), minLength: 0 })
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
module.exports.transport = require('./lib/transport')
|
|
224
|
-
module.exports.multistream = require('./lib/multistream')
|
|
225
|
-
|
|
226
|
-
module.exports.levels = mappings()
|
|
227
|
-
module.exports.stdSerializers = serializers
|
|
228
|
-
module.exports.stdTimeFunctions = Object.assign({}, time)
|
|
229
|
-
module.exports.symbols = symbols
|
|
230
|
-
module.exports.version = version
|
|
231
|
-
|
|
232
|
-
// Enables default and name export with TypeScript and Babel
|
|
233
|
-
module.exports.default = pino
|
|
234
|
-
module.exports.pino = pino
|
package/dist/lib/worker.js
DELETED
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const EE = require('node:events')
|
|
4
|
-
const { pipeline, PassThrough } = require('node:stream')
|
|
5
|
-
const pino = require('../pino.js')
|
|
6
|
-
const build = require('pino-abstract-transport')
|
|
7
|
-
const loadTransportStreamBuilder = require('./transport-stream')
|
|
8
|
-
|
|
9
|
-
// This file is not checked by the code coverage tool,
|
|
10
|
-
// as it is not reliable.
|
|
11
|
-
|
|
12
|
-
/* istanbul ignore file */
|
|
13
|
-
|
|
14
|
-
/*
|
|
15
|
-
* > Multiple targets & pipelines
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* ┌─────────────────────────────────────────────────┐ ┌─────┐
|
|
19
|
-
* │ │ │ p │
|
|
20
|
-
* │ │ │ i │
|
|
21
|
-
* │ target │ │ n │
|
|
22
|
-
* │ │ ────────────────────────────────┼────┤ o │
|
|
23
|
-
* │ targets │ target │ │ . │
|
|
24
|
-
* │ ────────────► │ ────────────────────────────────┼────┤ m │ source
|
|
25
|
-
* │ │ target │ │ u │ │
|
|
26
|
-
* │ │ ────────────────────────────────┼────┤ l │ │write
|
|
27
|
-
* │ │ │ │ t │ ▼
|
|
28
|
-
* │ │ pipeline ┌───────────────┐ │ │ i │ ┌────────┐
|
|
29
|
-
* │ │ ──────────► │ PassThrough ├───┼────┤ s ├──────┤ │
|
|
30
|
-
* │ │ └───────────────┘ │ │ t │ write│ Thread │
|
|
31
|
-
* │ │ │ │ r │◄─────┤ Stream │
|
|
32
|
-
* │ │ pipeline ┌───────────────┐ │ │ e │ │ │
|
|
33
|
-
* │ │ ──────────► │ PassThrough ├───┼────┤ a │ └────────┘
|
|
34
|
-
* │ └───────────────┘ │ │ m │
|
|
35
|
-
* │ │ │ │
|
|
36
|
-
* └─────────────────────────────────────────────────┘ └─────┘
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* > One single pipeline or target
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* source
|
|
44
|
-
* │
|
|
45
|
-
* ┌────────────────────────────────────────────────┐ │write
|
|
46
|
-
* │ │ ▼
|
|
47
|
-
* │ │ ┌────────┐
|
|
48
|
-
* │ targets │ target │ │ │
|
|
49
|
-
* │ ────────────► │ ──────────────────────────────┤ │ │
|
|
50
|
-
* │ │ │ │ │
|
|
51
|
-
* │ ├──────┤ │
|
|
52
|
-
* │ │ │ │
|
|
53
|
-
* │ │ │ │
|
|
54
|
-
* │ OR │ │ │
|
|
55
|
-
* │ │ │ │
|
|
56
|
-
* │ │ │ │
|
|
57
|
-
* │ ┌──────────────┐ │ │ │
|
|
58
|
-
* │ targets │ pipeline │ │ │ │ Thread │
|
|
59
|
-
* │ ────────────► │ ────────────►│ PassThrough ├─┤ │ Stream │
|
|
60
|
-
* │ │ │ │ │ │ │
|
|
61
|
-
* │ └──────────────┘ │ │ │
|
|
62
|
-
* │ │ │ │
|
|
63
|
-
* │ OR │ write│ │
|
|
64
|
-
* │ │◄─────┤ │
|
|
65
|
-
* │ │ │ │
|
|
66
|
-
* │ ┌──────────────┐ │ │ │
|
|
67
|
-
* │ pipeline │ │ │ │ │
|
|
68
|
-
* │ ──────────────►│ PassThrough ├────────────────┤ │ │
|
|
69
|
-
* │ │ │ │ │ │
|
|
70
|
-
* │ └──────────────┘ │ └────────┘
|
|
71
|
-
* │ │
|
|
72
|
-
* │ │
|
|
73
|
-
* └────────────────────────────────────────────────┘
|
|
74
|
-
*/
|
|
75
|
-
|
|
76
|
-
module.exports = async function ({ targets, pipelines, levels, dedupe }) {
|
|
77
|
-
const targetStreams = []
|
|
78
|
-
|
|
79
|
-
// Process targets
|
|
80
|
-
if (targets && targets.length) {
|
|
81
|
-
targets = await Promise.all(targets.map(async (t) => {
|
|
82
|
-
const fn = await loadTransportStreamBuilder(t.target)
|
|
83
|
-
const stream = await fn(t.options)
|
|
84
|
-
return {
|
|
85
|
-
level: t.level,
|
|
86
|
-
stream
|
|
87
|
-
}
|
|
88
|
-
}))
|
|
89
|
-
|
|
90
|
-
targetStreams.push(...targets)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Process pipelines
|
|
94
|
-
if (pipelines && pipelines.length) {
|
|
95
|
-
pipelines = await Promise.all(
|
|
96
|
-
pipelines.map(async (p) => {
|
|
97
|
-
let level
|
|
98
|
-
const pipeDests = await Promise.all(
|
|
99
|
-
p.map(async (t) => {
|
|
100
|
-
// level assigned to pipeline is duplicated over all its targets, just store it
|
|
101
|
-
level = t.level
|
|
102
|
-
const fn = await loadTransportStreamBuilder(t.target)
|
|
103
|
-
const stream = await fn(t.options)
|
|
104
|
-
return stream
|
|
105
|
-
}
|
|
106
|
-
))
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
level,
|
|
110
|
-
stream: createPipeline(pipeDests)
|
|
111
|
-
}
|
|
112
|
-
})
|
|
113
|
-
)
|
|
114
|
-
targetStreams.push(...pipelines)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Skip building the multistream step if either one single pipeline or target is defined and
|
|
118
|
-
// return directly the stream instance back to TreadStream.
|
|
119
|
-
// This is equivalent to define either:
|
|
120
|
-
//
|
|
121
|
-
// pino.transport({ target: ... })
|
|
122
|
-
//
|
|
123
|
-
// OR
|
|
124
|
-
//
|
|
125
|
-
// pino.transport({ pipeline: ... })
|
|
126
|
-
if (targetStreams.length === 1) {
|
|
127
|
-
return targetStreams[0].stream
|
|
128
|
-
} else {
|
|
129
|
-
return build(process, {
|
|
130
|
-
parse: 'lines',
|
|
131
|
-
metadata: true,
|
|
132
|
-
close (err, cb) {
|
|
133
|
-
let expected = 0
|
|
134
|
-
for (const transport of targetStreams) {
|
|
135
|
-
expected++
|
|
136
|
-
transport.stream.on('close', closeCb)
|
|
137
|
-
transport.stream.end()
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function closeCb () {
|
|
141
|
-
if (--expected === 0) {
|
|
142
|
-
cb(err)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
})
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// TODO: Why split2 was not used for pipelines?
|
|
150
|
-
function process (stream) {
|
|
151
|
-
const multi = pino.multistream(targetStreams, { levels, dedupe })
|
|
152
|
-
// TODO manage backpressure
|
|
153
|
-
stream.on('data', function (chunk) {
|
|
154
|
-
const { lastTime, lastMsg, lastObj, lastLevel } = this
|
|
155
|
-
multi.lastLevel = lastLevel
|
|
156
|
-
multi.lastTime = lastTime
|
|
157
|
-
multi.lastMsg = lastMsg
|
|
158
|
-
multi.lastObj = lastObj
|
|
159
|
-
|
|
160
|
-
// TODO handle backpressure
|
|
161
|
-
multi.write(chunk + '\n')
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Creates a pipeline using the provided streams and return an instance of `PassThrough` stream
|
|
167
|
-
* as a source for the pipeline.
|
|
168
|
-
*
|
|
169
|
-
* @param {(TransformStream|WritableStream)[]} streams An array of streams.
|
|
170
|
-
* All intermediate streams in the array *MUST* be `Transform` streams and only the last one `Writable`.
|
|
171
|
-
* @returns A `PassThrough` stream instance representing the source stream of the pipeline
|
|
172
|
-
*/
|
|
173
|
-
function createPipeline (streams) {
|
|
174
|
-
const ee = new EE()
|
|
175
|
-
const stream = new PassThrough({
|
|
176
|
-
autoDestroy: true,
|
|
177
|
-
destroy (_, cb) {
|
|
178
|
-
ee.on('error', cb)
|
|
179
|
-
ee.on('closed', cb)
|
|
180
|
-
}
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
pipeline(stream, ...streams, function (err) {
|
|
184
|
-
if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
|
|
185
|
-
ee.emit('error', err)
|
|
186
|
-
return
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
ee.emit('closed')
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
return stream
|
|
193
|
-
}
|
|
194
|
-
}
|