suemo 0.1.3 → 0.1.6
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 +48 -7
- package/package.json +1 -1
- package/skills/suemo/SKILL.md +1 -1
- package/skills/suemo/references/agents-snippet.md +1 -1
- package/skills/suemo/references/cli-reference.md +1 -1
- package/skills/suemo/references/core-workflow.md +1 -1
- package/skills/suemo/references/manual-test-plan.md +1 -1
- package/skills/suemo/references/mcp-reference.md +1 -1
- package/skills/suemo/references/schema-retention-longevity.md +1 -1
- package/skills/suemo/references/sync-local-vps.md +1 -1
- package/src/AGENTS.md +150 -0
- package/src/cli/commands/init.ts +510 -80
- package/src/cli/commands/query.ts +41 -3
- package/src/mcp/stdio.ts +3 -0
- package/src/memory/read.ts +392 -77
- package/src/types.ts +32 -4
package/src/cli/commands/init.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, wr
|
|
|
12
12
|
import { tmpdir } from 'node:os'
|
|
13
13
|
import { basename, dirname, join, resolve as resolvePath } from 'node:path'
|
|
14
14
|
|
|
15
|
+
import OPENCODE_AGENTS_SNIPPET_TEXT from '@/src/AGENTS.md' with { type: 'text' }
|
|
15
16
|
import template from '@/src/config.template' with { type: 'text' }
|
|
16
17
|
import FASTEMBED_SCRIPT_TEXT from '@/src/embedding/fastembed-server.py' with { type: 'text' }
|
|
17
18
|
|
|
@@ -32,13 +33,30 @@ const init = app.sub('init')
|
|
|
32
33
|
const SURREAL_PROFILES_DIR = '/opt/suemo/surreal'
|
|
33
34
|
const SURREAL_LOCAL_ENV_PATH = '/opt/suemo/surreal/local.env'
|
|
34
35
|
const SURREAL_SYSTEMD_UNIT_PATH = '/etc/systemd/system/suemo-surreal@.service'
|
|
36
|
+
const SURREAL_RUNIT_SERVICE_BASE_DIR = '/etc/sv'
|
|
37
|
+
const SURREAL_SERVICE_NAME_PREFIX = 'suemo-surreal'
|
|
35
38
|
const SURREAL_DATA_DIR = '/var/lib/surrealdb'
|
|
36
39
|
const FASTEMBED_INSTALL_DIR = '/opt/suemo'
|
|
37
|
-
const FASTEMBED_LOCAL_ENV_DIR = '/opt/fastembed'
|
|
40
|
+
const FASTEMBED_LOCAL_ENV_DIR = '/opt/suemo/fastembed'
|
|
41
|
+
const FASTEMBED_COMMON_ENV_PATH = '/opt/suemo/fastembed/common.env'
|
|
38
42
|
const FASTEMBED_SCRIPT_TARGET = '/opt/suemo/fastembed-server.py'
|
|
39
43
|
const FASTEMBED_CACHE_DIR = '/var/cache/fastembed'
|
|
40
44
|
const FASTEMBED_SERVICE_PATH = '/etc/systemd/system/suemo-fastembed.service'
|
|
45
|
+
const FASTEMBED_RUNIT_SERVICE_BASE_DIR = '/etc/sv'
|
|
46
|
+
const FASTEMBED_SERVICE_NAME = 'suemo-fastembed'
|
|
41
47
|
const FASTEMBED_USER_HOME = '/var/lib/fastembed'
|
|
48
|
+
const RUNIT_SERVICE_DIR_CANDIDATES = ['/var/service', '/service', '/run/runit/service'] as const
|
|
49
|
+
const DEFAULT_RUNIT_SERVICE_DIR = '/var/service'
|
|
50
|
+
|
|
51
|
+
const SUEMO_AGENTS_START_MARKER = '<!-- SUEMO:START -->'
|
|
52
|
+
const SUEMO_AGENTS_END_MARKER = '<!-- SUEMO:END -->'
|
|
53
|
+
|
|
54
|
+
const FASTEMBED_DEFAULT_ENV = {
|
|
55
|
+
FASTEMBED_HOST: '127.0.0.1',
|
|
56
|
+
FASTEMBED_PORT: '8080',
|
|
57
|
+
FASTEMBED_MODEL: 'sentence-transformers/all-MiniLM-L6-v2',
|
|
58
|
+
FASTEMBED_CACHE_DIR: '/var/cache/fastembed',
|
|
59
|
+
}
|
|
42
60
|
|
|
43
61
|
const SURREAL_TEMPLATE_UNIT = `# Generated by suemo init surreal
|
|
44
62
|
[Unit]
|
|
@@ -118,7 +136,7 @@ SURREAL_TRANSACTION_TIMEOUT=15s
|
|
|
118
136
|
|
|
119
137
|
# Capability allowlist (required for suemo workflows)
|
|
120
138
|
SURREAL_CAPS_DENY_ALL=true
|
|
121
|
-
SURREAL_CAPS_ALLOW_FUNC=fn,time,vector,search,math,rand,ml
|
|
139
|
+
SURREAL_CAPS_ALLOW_FUNC=fn,time,vector,search,math,rand,ml,array
|
|
122
140
|
|
|
123
141
|
# Logging
|
|
124
142
|
SURREAL_LOG=warn
|
|
@@ -165,16 +183,19 @@ SURREAL_EXTERNAL_SORTING_BUFFER_LIMIT=25000
|
|
|
165
183
|
SURREAL_MEMORY_THRESHOLD=512m
|
|
166
184
|
`
|
|
167
185
|
|
|
168
|
-
const FASTEMBED_LOCAL_ENV_PATH = '/opt/fastembed/local.env'
|
|
186
|
+
const FASTEMBED_LOCAL_ENV_PATH = '/opt/suemo/fastembed/local.env'
|
|
187
|
+
|
|
188
|
+
const FASTEMBED_COMMON_ENV_TEMPLATE = `# Generated by suemo init fastembed
|
|
189
|
+
# DO NOT edit - managed by suemo
|
|
190
|
+
|
|
191
|
+
${Object.entries(FASTEMBED_DEFAULT_ENV).map(([key, value]) => `${key}=${value}`).join('\n')}
|
|
192
|
+
`
|
|
169
193
|
|
|
170
194
|
const FASTEMBED_LOCAL_ENV_TEMPLATE = `# User overrides - edit this file to customize settings
|
|
171
195
|
# This file is NOT overwritten by suemo init fastembed
|
|
172
196
|
# Uncomment and set values to override defaults
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
#FASTEMBED_PORT=8080
|
|
176
|
-
#FASTEMBED_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
177
|
-
#FASTEMBED_CACHE_DIR=/var/cache/fastembed
|
|
198
|
+
${Object.entries(FASTEMBED_DEFAULT_ENV).map(([key, value]) => `#${key}=${value}`).join('\n')}
|
|
178
199
|
`
|
|
179
200
|
|
|
180
201
|
const FASTEMBED_SYSTEMD_SERVICE = `# Generated by suemo init fastembed
|
|
@@ -188,11 +209,8 @@ AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
|
188
209
|
User=fastembed
|
|
189
210
|
Group=fastembed
|
|
190
211
|
WorkingDirectory=/opt/suemo
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
Environment=FASTEMBED_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
194
|
-
Environment=FASTEMBED_CACHE_DIR=/var/cache/fastembed
|
|
195
|
-
EnvironmentFile=-/opt/fastembed/local.env
|
|
212
|
+
EnvironmentFile=/opt/suemo/fastembed/common.env
|
|
213
|
+
EnvironmentFile=-/opt/suemo/fastembed/local.env
|
|
196
214
|
ExecStart=/usr/bin/python /opt/suemo/fastembed-server.py
|
|
197
215
|
Restart=on-failure
|
|
198
216
|
RestartSec=5s
|
|
@@ -200,7 +218,7 @@ NoNewPrivileges=true
|
|
|
200
218
|
PrivateTmp=true
|
|
201
219
|
ProtectSystem=strict
|
|
202
220
|
ProtectHome=true
|
|
203
|
-
ReadWritePaths=/var/cache/fastembed
|
|
221
|
+
ReadWritePaths=/var/cache/fastembed /var/lib/fastembed
|
|
204
222
|
StandardOutput=journal
|
|
205
223
|
StandardError=journal
|
|
206
224
|
SyslogIdentifier=suemo-fastembed
|
|
@@ -209,6 +227,50 @@ SyslogIdentifier=suemo-fastembed
|
|
|
209
227
|
WantedBy=multi-user.target
|
|
210
228
|
`
|
|
211
229
|
|
|
230
|
+
const SURREAL_RUNIT_RUN_TEMPLATE = `#!/bin/sh
|
|
231
|
+
set -a
|
|
232
|
+
. /opt/suemo/surreal/common.env
|
|
233
|
+
. /opt/suemo/surreal/__PROFILE__.env
|
|
234
|
+
[ -r /opt/suemo/surreal/local.env ] && . /opt/suemo/surreal/local.env
|
|
235
|
+
set +a
|
|
236
|
+
exec 2>&1
|
|
237
|
+
exec chpst -u surrealdb:surrealdb /usr/bin/surreal start
|
|
238
|
+
`
|
|
239
|
+
|
|
240
|
+
const FASTEMBED_RUNIT_RUN_TEMPLATE = `#!/bin/sh
|
|
241
|
+
set -a
|
|
242
|
+
. /opt/suemo/fastembed/common.env
|
|
243
|
+
[ -r /opt/suemo/fastembed/local.env ] && . /opt/suemo/fastembed/local.env
|
|
244
|
+
set +a
|
|
245
|
+
exec 2>&1
|
|
246
|
+
exec chpst -u fastembed:fastembed /usr/bin/python /opt/suemo/fastembed-server.py
|
|
247
|
+
`
|
|
248
|
+
|
|
249
|
+
type ServiceManager =
|
|
250
|
+
| { kind: 'systemd' }
|
|
251
|
+
| { kind: 'runit'; serviceDir: string }
|
|
252
|
+
|
|
253
|
+
type OpenCodeConfigMode = 'mcp'
|
|
254
|
+
type OpenCodeConfigStatus = 'added' | 'already-present' | 'unchanged'
|
|
255
|
+
type OpenCodeAgentsStatus = 'added' | 'updated' | 'unchanged'
|
|
256
|
+
|
|
257
|
+
interface OpenCodeConfigResolution {
|
|
258
|
+
path: string
|
|
259
|
+
format: 'jsonc' | 'json'
|
|
260
|
+
existed: boolean
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
interface OpenCodeInitResult {
|
|
264
|
+
openCodeDir: string
|
|
265
|
+
configPath: string
|
|
266
|
+
configFormat: 'jsonc' | 'json'
|
|
267
|
+
configStatus: OpenCodeConfigStatus
|
|
268
|
+
configMode: OpenCodeConfigMode
|
|
269
|
+
agentsPath: string
|
|
270
|
+
agentsStatus: OpenCodeAgentsStatus
|
|
271
|
+
dryRun: boolean
|
|
272
|
+
}
|
|
273
|
+
|
|
212
274
|
type InitAction =
|
|
213
275
|
| {
|
|
214
276
|
kind: 'mkdir'
|
|
@@ -233,6 +295,12 @@ type CredentialStatus = 'preserved' | 'generated'
|
|
|
233
295
|
interface SurrealInitResult {
|
|
234
296
|
actions: InitAction[]
|
|
235
297
|
credentialStatus: CredentialStatus
|
|
298
|
+
serviceName: string
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface FastembedInitResult {
|
|
302
|
+
actions: InitAction[]
|
|
303
|
+
serviceName: string
|
|
236
304
|
}
|
|
237
305
|
|
|
238
306
|
function modeText(mode: number): string {
|
|
@@ -285,6 +353,269 @@ function requireArchPackages(packages: string[]): void {
|
|
|
285
353
|
}
|
|
286
354
|
}
|
|
287
355
|
|
|
356
|
+
function detectServiceManager(): ServiceManager {
|
|
357
|
+
const hasSystemd = commandExists('systemctl') && existsSync('/run/systemd/system')
|
|
358
|
+
if (hasSystemd) return { kind: 'systemd' }
|
|
359
|
+
|
|
360
|
+
const runitServiceDir = RUNIT_SERVICE_DIR_CANDIDATES.find((candidate) => existsSync(candidate))
|
|
361
|
+
const hasRunitTooling = commandExists('sv') || commandExists('runsvdir') || commandExists('chpst')
|
|
362
|
+
if (runitServiceDir || hasRunitTooling) {
|
|
363
|
+
return {
|
|
364
|
+
kind: 'runit',
|
|
365
|
+
serviceDir: runitServiceDir ?? DEFAULT_RUNIT_SERVICE_DIR,
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
throw new Error(
|
|
370
|
+
'systemd is not detected. Install runit first (e.g. `sudo pacman -S runit`) and ensure /var/service (or /service) exists.',
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function ensureObjectValue(
|
|
375
|
+
container: Record<string, unknown>,
|
|
376
|
+
key: string,
|
|
377
|
+
defaultValue: Record<string, unknown>,
|
|
378
|
+
): Record<string, unknown> {
|
|
379
|
+
const existing = container[key]
|
|
380
|
+
if (existing && typeof existing === 'object' && !Array.isArray(existing)) {
|
|
381
|
+
return existing as Record<string, unknown>
|
|
382
|
+
}
|
|
383
|
+
container[key] = defaultValue
|
|
384
|
+
return defaultValue
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function stripJsonComments(input: string): string {
|
|
388
|
+
let out = ''
|
|
389
|
+
let inString = false
|
|
390
|
+
let escaped = false
|
|
391
|
+
let inLineComment = false
|
|
392
|
+
let inBlockComment = false
|
|
393
|
+
|
|
394
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
395
|
+
const ch = input[i]!
|
|
396
|
+
const next = input[i + 1]
|
|
397
|
+
|
|
398
|
+
if (inLineComment) {
|
|
399
|
+
if (ch === '\n') {
|
|
400
|
+
inLineComment = false
|
|
401
|
+
out += ch
|
|
402
|
+
}
|
|
403
|
+
continue
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
if (inBlockComment) {
|
|
407
|
+
if (ch === '*' && next === '/') {
|
|
408
|
+
inBlockComment = false
|
|
409
|
+
i += 1
|
|
410
|
+
}
|
|
411
|
+
continue
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (inString) {
|
|
415
|
+
out += ch
|
|
416
|
+
if (escaped) {
|
|
417
|
+
escaped = false
|
|
418
|
+
continue
|
|
419
|
+
}
|
|
420
|
+
if (ch === '\\') {
|
|
421
|
+
escaped = true
|
|
422
|
+
continue
|
|
423
|
+
}
|
|
424
|
+
if (ch === '"') {
|
|
425
|
+
inString = false
|
|
426
|
+
}
|
|
427
|
+
continue
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (ch === '"') {
|
|
431
|
+
inString = true
|
|
432
|
+
out += ch
|
|
433
|
+
continue
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (ch === '/' && next === '/') {
|
|
437
|
+
inLineComment = true
|
|
438
|
+
i += 1
|
|
439
|
+
continue
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (ch === '/' && next === '*') {
|
|
443
|
+
inBlockComment = true
|
|
444
|
+
i += 1
|
|
445
|
+
continue
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
out += ch
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return out
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function parseJsoncObject(content: string, filePath: string): Record<string, unknown> {
|
|
455
|
+
const withoutComments = stripJsonComments(content)
|
|
456
|
+
const normalized = withoutComments.replace(/,\s*([}\]])/g, '$1')
|
|
457
|
+
let parsed: unknown
|
|
458
|
+
try {
|
|
459
|
+
parsed = JSON.parse(normalized)
|
|
460
|
+
} catch (error) {
|
|
461
|
+
throw new Error(`Failed to parse ${filePath}: ${error instanceof Error ? error.message : String(error)}`)
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
465
|
+
throw new Error(`Expected top-level object in ${filePath}`)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return parsed as Record<string, unknown>
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function resolveOpenCodeConfig(openCodeDir: string): OpenCodeConfigResolution {
|
|
472
|
+
const jsoncPath = join(openCodeDir, 'opencode.jsonc')
|
|
473
|
+
if (existsSync(jsoncPath)) {
|
|
474
|
+
return { path: jsoncPath, format: 'jsonc', existed: true }
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const jsonPath = join(openCodeDir, 'opencode.json')
|
|
478
|
+
if (existsSync(jsonPath)) {
|
|
479
|
+
return { path: jsonPath, format: 'json', existed: true }
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return { path: jsoncPath, format: 'jsonc', existed: false }
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function upsertSuemoOpenCodeConfig(config: Record<string, unknown>, configPath: string): {
|
|
486
|
+
status: OpenCodeConfigStatus
|
|
487
|
+
mode: OpenCodeConfigMode
|
|
488
|
+
changed: boolean
|
|
489
|
+
} {
|
|
490
|
+
const defaultCommand = ['suemo', 'serve', '--stdio', '--config', configPath]
|
|
491
|
+
const mcp = ensureObjectValue(config, 'mcp', {})
|
|
492
|
+
const hasMcpSuemo = Boolean(mcp.suemo && typeof mcp.suemo === 'object')
|
|
493
|
+
|
|
494
|
+
const mcpServers = config.mcpServers
|
|
495
|
+
if (mcpServers && typeof mcpServers === 'object' && !Array.isArray(mcpServers)) {
|
|
496
|
+
const servers = mcpServers as Record<string, unknown>
|
|
497
|
+
const legacy = servers.suemo
|
|
498
|
+
if (legacy && typeof legacy === 'object' && !Array.isArray(legacy)) {
|
|
499
|
+
if (!hasMcpSuemo) {
|
|
500
|
+
const legacyRecord = legacy as Record<string, unknown>
|
|
501
|
+
const legacyCommand = legacyRecord.command
|
|
502
|
+
const legacyArgs = legacyRecord.args
|
|
503
|
+
if (
|
|
504
|
+
typeof legacyCommand === 'string' && Array.isArray(legacyArgs)
|
|
505
|
+
&& legacyArgs.every((arg) => typeof arg === 'string')
|
|
506
|
+
) {
|
|
507
|
+
mcp.suemo = {
|
|
508
|
+
type: 'local',
|
|
509
|
+
command: [legacyCommand, ...(legacyArgs as string[])],
|
|
510
|
+
enabled: true,
|
|
511
|
+
}
|
|
512
|
+
} else {
|
|
513
|
+
mcp.suemo = {
|
|
514
|
+
type: 'local',
|
|
515
|
+
command: defaultCommand,
|
|
516
|
+
enabled: true,
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
delete servers.suemo
|
|
520
|
+
return { status: 'added', mode: 'mcp', changed: true }
|
|
521
|
+
}
|
|
522
|
+
delete servers.suemo
|
|
523
|
+
return { status: 'already-present', mode: 'mcp', changed: true }
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (hasMcpSuemo) {
|
|
528
|
+
return { status: 'already-present', mode: 'mcp', changed: false }
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
mcp.suemo = {
|
|
532
|
+
type: 'local',
|
|
533
|
+
command: defaultCommand,
|
|
534
|
+
enabled: true,
|
|
535
|
+
}
|
|
536
|
+
return { status: 'added', mode: 'mcp', changed: true }
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function renderAgentsSnippetBlock(): string {
|
|
540
|
+
return [SUEMO_AGENTS_START_MARKER, OPENCODE_AGENTS_SNIPPET_TEXT.trim(), SUEMO_AGENTS_END_MARKER, ''].join('\n')
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function upsertAgentsSnippet(existing: string): { content: string; status: OpenCodeAgentsStatus } {
|
|
544
|
+
const snippet = renderAgentsSnippetBlock()
|
|
545
|
+
const start = existing.indexOf(SUEMO_AGENTS_START_MARKER)
|
|
546
|
+
const end = existing.indexOf(SUEMO_AGENTS_END_MARKER)
|
|
547
|
+
|
|
548
|
+
if (start !== -1 && end !== -1 && end > start) {
|
|
549
|
+
const blockEnd = end + SUEMO_AGENTS_END_MARKER.length
|
|
550
|
+
const currentBlock = existing.slice(start, blockEnd).trim()
|
|
551
|
+
const nextBlock = snippet.trim()
|
|
552
|
+
if (currentBlock === nextBlock) {
|
|
553
|
+
return { content: existing, status: 'unchanged' }
|
|
554
|
+
}
|
|
555
|
+
const before = existing.slice(0, start).replace(/\s*$/, '\n\n')
|
|
556
|
+
const after = existing.slice(blockEnd).replace(/^\s*/, '\n\n')
|
|
557
|
+
return {
|
|
558
|
+
content: `${before}${snippet}${after}`.replace(/\n{3,}/g, '\n\n').trimEnd() + '\n',
|
|
559
|
+
status: 'updated',
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (existing.includes(SUEMO_AGENTS_START_MARKER) || existing.includes(SUEMO_AGENTS_END_MARKER)) {
|
|
564
|
+
const cleaned = existing
|
|
565
|
+
.replace(SUEMO_AGENTS_START_MARKER, '')
|
|
566
|
+
.replace(SUEMO_AGENTS_END_MARKER, '')
|
|
567
|
+
.trimEnd()
|
|
568
|
+
return { content: `${cleaned}\n\n${snippet}`.replace(/\n{3,}/g, '\n\n'), status: 'updated' }
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (existing.trim().length === 0) {
|
|
572
|
+
return { content: snippet, status: 'added' }
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
content: `${existing.trimEnd()}\n\n${snippet}`.replace(/\n{3,}/g, '\n\n'),
|
|
577
|
+
status: 'added',
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function initOpenCodeFiles(configPath: string, dryRun: boolean): OpenCodeInitResult {
|
|
582
|
+
const home = process.env.HOME ?? process.env.USERPROFILE
|
|
583
|
+
if (!home) throw new Error('HOME/USERPROFILE is not set; cannot resolve OpenCode config path')
|
|
584
|
+
|
|
585
|
+
const openCodeDir = join(home, '.config', 'opencode')
|
|
586
|
+
const configResolution = resolveOpenCodeConfig(openCodeDir)
|
|
587
|
+
const rawConfig = configResolution.existed ? readFileSync(configResolution.path, 'utf-8') : '{}\n'
|
|
588
|
+
const parsedConfig = parseJsoncObject(rawConfig, configResolution.path)
|
|
589
|
+
const configWriteResult = upsertSuemoOpenCodeConfig(parsedConfig, configPath)
|
|
590
|
+
const nextConfigText = `${JSON.stringify(parsedConfig, null, '\t')}\n`
|
|
591
|
+
const configStatus: OpenCodeConfigStatus = configWriteResult.changed ? configWriteResult.status : 'unchanged'
|
|
592
|
+
|
|
593
|
+
const agentsPath = join(openCodeDir, 'AGENTS.md')
|
|
594
|
+
const existingAgents = existsSync(agentsPath) ? readFileSync(agentsPath, 'utf-8') : ''
|
|
595
|
+
const agentsResult = upsertAgentsSnippet(existingAgents)
|
|
596
|
+
|
|
597
|
+
if (!dryRun) {
|
|
598
|
+
mkdirSync(openCodeDir, { recursive: true })
|
|
599
|
+
if (!configResolution.existed || configWriteResult.changed) {
|
|
600
|
+
writeFileSync(configResolution.path, nextConfigText, 'utf-8')
|
|
601
|
+
}
|
|
602
|
+
if (!existsSync(agentsPath) || agentsResult.content !== existingAgents) {
|
|
603
|
+
writeFileSync(agentsPath, agentsResult.content, 'utf-8')
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return {
|
|
608
|
+
openCodeDir,
|
|
609
|
+
configPath: configResolution.path,
|
|
610
|
+
configFormat: configResolution.format,
|
|
611
|
+
configStatus,
|
|
612
|
+
configMode: configWriteResult.mode,
|
|
613
|
+
agentsPath,
|
|
614
|
+
agentsStatus: agentsResult.status,
|
|
615
|
+
dryRun,
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
288
619
|
function commandExists(command: string): boolean {
|
|
289
620
|
const result = spawnSync('sh', ['-lc', `command -v ${command}`], { stdio: 'ignore' })
|
|
290
621
|
return (result.status ?? 1) === 0
|
|
@@ -393,15 +724,38 @@ function extractEnvValue(content: string, key: string): string | null {
|
|
|
393
724
|
return match[1].trim()
|
|
394
725
|
}
|
|
395
726
|
|
|
727
|
+
function runitServiceDefinitionPath(baseDir: string, serviceName: string): string {
|
|
728
|
+
return join(baseDir, serviceName)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function runitServiceLinkPath(serviceDir: string, serviceName: string): string {
|
|
732
|
+
return join(serviceDir, serviceName)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function buildRunitEnableActions(
|
|
736
|
+
baseDir: string,
|
|
737
|
+
serviceName: string,
|
|
738
|
+
manager: Extract<ServiceManager, { kind: 'runit' }>,
|
|
739
|
+
): InitAction[] {
|
|
740
|
+
const definitionPath = runitServiceDefinitionPath(baseDir, serviceName)
|
|
741
|
+
const linkPath = runitServiceLinkPath(manager.serviceDir, serviceName)
|
|
742
|
+
return [
|
|
743
|
+
{ kind: 'mkdir', path: manager.serviceDir, mode: 0o755 },
|
|
744
|
+
{ kind: 'run', command: 'ln', args: ['-sfn', definitionPath, linkPath], requireRoot: true },
|
|
745
|
+
]
|
|
746
|
+
}
|
|
747
|
+
|
|
396
748
|
function buildSurrealActions(
|
|
397
749
|
profile: '2gb' | '6gb',
|
|
398
750
|
existingPassword: string | null,
|
|
399
751
|
force: boolean,
|
|
752
|
+
manager: ServiceManager,
|
|
400
753
|
): SurrealInitResult {
|
|
401
754
|
const commonEnvPath = join(SURREAL_PROFILES_DIR, 'common.env')
|
|
402
755
|
const profileEnvPath = join(SURREAL_PROFILES_DIR, `${profile}.env`)
|
|
403
756
|
const localEnvPath = SURREAL_LOCAL_ENV_PATH
|
|
404
757
|
const actions: InitAction[] = []
|
|
758
|
+
const runitServiceName = `${SURREAL_SERVICE_NAME_PREFIX}-${profile}`
|
|
405
759
|
|
|
406
760
|
log.debug('buildSurrealActions: start', { profile, existingPassword: existingPassword ? '***' : null, force })
|
|
407
761
|
|
|
@@ -458,6 +812,32 @@ function buildSurrealActions(
|
|
|
458
812
|
|
|
459
813
|
const localEnvExists = existsSync(localEnvPath)
|
|
460
814
|
|
|
815
|
+
const managerActions: InitAction[] = manager.kind === 'systemd'
|
|
816
|
+
? [
|
|
817
|
+
{ kind: 'write', path: SURREAL_SYSTEMD_UNIT_PATH, mode: 0o644, content: SURREAL_TEMPLATE_UNIT },
|
|
818
|
+
{ kind: 'run', command: 'systemctl', args: ['daemon-reload'], requireRoot: true },
|
|
819
|
+
{
|
|
820
|
+
kind: 'run',
|
|
821
|
+
command: 'systemctl',
|
|
822
|
+
args: ['enable', '--now', `suemo-surreal@${profile}.service`],
|
|
823
|
+
requireRoot: true,
|
|
824
|
+
},
|
|
825
|
+
]
|
|
826
|
+
: [
|
|
827
|
+
{
|
|
828
|
+
kind: 'mkdir',
|
|
829
|
+
path: runitServiceDefinitionPath(SURREAL_RUNIT_SERVICE_BASE_DIR, runitServiceName),
|
|
830
|
+
mode: 0o755,
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
kind: 'write',
|
|
834
|
+
path: join(runitServiceDefinitionPath(SURREAL_RUNIT_SERVICE_BASE_DIR, runitServiceName), 'run'),
|
|
835
|
+
mode: 0o755,
|
|
836
|
+
content: SURREAL_RUNIT_RUN_TEMPLATE.replace('__PROFILE__', profile),
|
|
837
|
+
},
|
|
838
|
+
...buildRunitEnableActions(SURREAL_RUNIT_SERVICE_BASE_DIR, runitServiceName, manager),
|
|
839
|
+
]
|
|
840
|
+
|
|
461
841
|
actions.push(
|
|
462
842
|
{ kind: 'mkdir', path: SURREAL_PROFILES_DIR, mode: 0o750 },
|
|
463
843
|
{ kind: 'mkdir', path: SURREAL_DATA_DIR, mode: 0o750 },
|
|
@@ -466,7 +846,7 @@ function buildSurrealActions(
|
|
|
466
846
|
...(!localEnvExists
|
|
467
847
|
? [{ kind: 'write' as const, path: localEnvPath, mode: 0o644, content: SURREAL_LOCAL_ENV_TEMPLATE }]
|
|
468
848
|
: []),
|
|
469
|
-
|
|
849
|
+
...managerActions,
|
|
470
850
|
{ kind: 'run', command: 'chown', args: ['-R', 'surrealdb:surrealdb', SURREAL_DATA_DIR], requireRoot: true },
|
|
471
851
|
{
|
|
472
852
|
kind: 'run',
|
|
@@ -480,22 +860,20 @@ function buildSurrealActions(
|
|
|
480
860
|
args: ['root:surrealdb', profileEnvPath],
|
|
481
861
|
requireRoot: true,
|
|
482
862
|
},
|
|
483
|
-
{ kind: 'run', command: 'systemctl', args: ['daemon-reload'], requireRoot: true },
|
|
484
|
-
{
|
|
485
|
-
kind: 'run',
|
|
486
|
-
command: 'systemctl',
|
|
487
|
-
args: ['enable', '--now', `suemo-surreal@${profile}.service`],
|
|
488
|
-
requireRoot: true,
|
|
489
|
-
},
|
|
490
863
|
)
|
|
491
864
|
|
|
492
865
|
log.debug('buildSurrealActions: complete', { actionCount: actions.length })
|
|
493
|
-
return {
|
|
866
|
+
return {
|
|
867
|
+
actions,
|
|
868
|
+
credentialStatus,
|
|
869
|
+
serviceName: manager.kind === 'systemd' ? `suemo-surreal@${profile}.service` : runitServiceName,
|
|
870
|
+
}
|
|
494
871
|
}
|
|
495
872
|
|
|
496
|
-
function buildFastembedActions(scriptContent: string):
|
|
873
|
+
function buildFastembedActions(scriptContent: string, manager: ServiceManager): FastembedInitResult {
|
|
497
874
|
const actions: InitAction[] = []
|
|
498
875
|
const localEnvExists = existsSync(FASTEMBED_LOCAL_ENV_PATH)
|
|
876
|
+
const runitServiceName = FASTEMBED_SERVICE_NAME
|
|
499
877
|
|
|
500
878
|
if (!systemUserExists('fastembed')) {
|
|
501
879
|
actions.push({
|
|
@@ -514,37 +892,69 @@ function buildFastembedActions(scriptContent: string): InitAction[] {
|
|
|
514
892
|
})
|
|
515
893
|
}
|
|
516
894
|
|
|
895
|
+
const managerActions: InitAction[] = manager.kind === 'systemd'
|
|
896
|
+
? [
|
|
897
|
+
{ kind: 'write', path: FASTEMBED_SERVICE_PATH, mode: 0o644, content: FASTEMBED_SYSTEMD_SERVICE },
|
|
898
|
+
{ kind: 'run', command: 'systemctl', args: ['daemon-reload'], requireRoot: true },
|
|
899
|
+
{ kind: 'run', command: 'systemctl', args: ['enable', '--now', FASTEMBED_SERVICE_NAME], requireRoot: true },
|
|
900
|
+
]
|
|
901
|
+
: [
|
|
902
|
+
{
|
|
903
|
+
kind: 'mkdir',
|
|
904
|
+
path: runitServiceDefinitionPath(FASTEMBED_RUNIT_SERVICE_BASE_DIR, runitServiceName),
|
|
905
|
+
mode: 0o755,
|
|
906
|
+
},
|
|
907
|
+
{
|
|
908
|
+
kind: 'write',
|
|
909
|
+
path: join(runitServiceDefinitionPath(FASTEMBED_RUNIT_SERVICE_BASE_DIR, runitServiceName), 'run'),
|
|
910
|
+
mode: 0o755,
|
|
911
|
+
content: FASTEMBED_RUNIT_RUN_TEMPLATE,
|
|
912
|
+
},
|
|
913
|
+
...buildRunitEnableActions(FASTEMBED_RUNIT_SERVICE_BASE_DIR, runitServiceName, manager),
|
|
914
|
+
]
|
|
915
|
+
|
|
517
916
|
actions.push(
|
|
518
917
|
{ kind: 'mkdir', path: FASTEMBED_INSTALL_DIR, mode: 0o755 },
|
|
519
918
|
{ kind: 'mkdir', path: FASTEMBED_LOCAL_ENV_DIR, mode: 0o755 },
|
|
520
919
|
{ kind: 'mkdir', path: FASTEMBED_CACHE_DIR, mode: 0o755 },
|
|
920
|
+
{ kind: 'write', path: FASTEMBED_COMMON_ENV_PATH, mode: 0o644, content: FASTEMBED_COMMON_ENV_TEMPLATE },
|
|
521
921
|
{ kind: 'write', path: FASTEMBED_SCRIPT_TARGET, mode: 0o755, content: scriptContent },
|
|
522
922
|
...(!localEnvExists
|
|
523
923
|
? [{ kind: 'write' as const, path: FASTEMBED_LOCAL_ENV_PATH, mode: 0o644, content: FASTEMBED_LOCAL_ENV_TEMPLATE }]
|
|
524
924
|
: []),
|
|
525
|
-
|
|
925
|
+
...managerActions,
|
|
526
926
|
{ kind: 'run', command: 'chown', args: ['-R', 'fastembed:fastembed', FASTEMBED_INSTALL_DIR], requireRoot: true },
|
|
927
|
+
{ kind: 'run', command: 'chown', args: ['root:root', FASTEMBED_COMMON_ENV_PATH], requireRoot: true },
|
|
527
928
|
{ kind: 'run', command: 'chown', args: ['root:root', FASTEMBED_LOCAL_ENV_DIR], requireRoot: true },
|
|
528
929
|
{ kind: 'run', command: 'chown', args: ['root:root', FASTEMBED_LOCAL_ENV_PATH], requireRoot: true },
|
|
529
930
|
{ kind: 'run', command: 'chown', args: ['-R', 'fastembed:fastembed', FASTEMBED_CACHE_DIR], requireRoot: true },
|
|
530
|
-
{ kind: 'run', command: 'systemctl', args: ['daemon-reload'], requireRoot: true },
|
|
531
|
-
{ kind: 'run', command: 'systemctl', args: ['enable', '--now', 'suemo-fastembed.service'], requireRoot: true },
|
|
532
931
|
)
|
|
533
932
|
|
|
534
|
-
return
|
|
933
|
+
return {
|
|
934
|
+
actions,
|
|
935
|
+
serviceName: manager.kind === 'systemd' ? `${FASTEMBED_SERVICE_NAME}.service` : runitServiceName,
|
|
936
|
+
}
|
|
535
937
|
}
|
|
536
938
|
|
|
537
|
-
function printInitSystemSummary(
|
|
939
|
+
function printInitSystemSummary(
|
|
940
|
+
kind: 'surreal' | 'fastembed',
|
|
941
|
+
dryRun: boolean,
|
|
942
|
+
manager: ServiceManager,
|
|
943
|
+
serviceName: string,
|
|
944
|
+
): void {
|
|
538
945
|
if (kind === 'surreal') {
|
|
539
946
|
if (dryRun) {
|
|
540
947
|
console.log('Dry-run complete for SurrealDB setup.')
|
|
541
948
|
} else {
|
|
542
949
|
console.log('✓ SurrealDB setup complete.')
|
|
543
950
|
}
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
console.log(`Status: systemctl status
|
|
547
|
-
console.log(`Logs: journalctl -u
|
|
951
|
+
console.log(`Service: ${serviceName}`)
|
|
952
|
+
if (manager.kind === 'systemd') {
|
|
953
|
+
console.log(`Status: systemctl status ${serviceName}`)
|
|
954
|
+
console.log(`Logs: journalctl -u ${serviceName} -f`)
|
|
955
|
+
} else {
|
|
956
|
+
console.log(`Status: sv status ${serviceName}`)
|
|
957
|
+
console.log(`Control: sv up ${serviceName} | sv down ${serviceName}`)
|
|
548
958
|
}
|
|
549
959
|
return
|
|
550
960
|
}
|
|
@@ -554,9 +964,14 @@ function printInitSystemSummary(kind: 'surreal' | 'fastembed', dryRun: boolean,
|
|
|
554
964
|
} else {
|
|
555
965
|
console.log('✓ fastembed setup complete.')
|
|
556
966
|
}
|
|
557
|
-
console.log(
|
|
558
|
-
|
|
559
|
-
|
|
967
|
+
console.log(`Service: ${serviceName}`)
|
|
968
|
+
if (manager.kind === 'systemd') {
|
|
969
|
+
console.log(`Status: systemctl status ${serviceName}`)
|
|
970
|
+
console.log(`Logs: journalctl -u ${serviceName} -f`)
|
|
971
|
+
} else {
|
|
972
|
+
console.log(`Status: sv status ${serviceName}`)
|
|
973
|
+
console.log(`Control: sv up ${serviceName} | sv down ${serviceName}`)
|
|
974
|
+
}
|
|
560
975
|
}
|
|
561
976
|
|
|
562
977
|
function homeConfigPath(): string {
|
|
@@ -714,8 +1129,9 @@ const initSchemaCmd = init.sub('schema')
|
|
|
714
1129
|
})
|
|
715
1130
|
|
|
716
1131
|
const initOpenCodeCmd = init.sub('opencode')
|
|
717
|
-
.meta({ description: '
|
|
1132
|
+
.meta({ description: 'Initialize OpenCode MCP config + AGENTS.md suemo snippet' })
|
|
718
1133
|
.flags({
|
|
1134
|
+
'dry-run': { type: 'boolean', description: 'Show changes without writing files', default: false },
|
|
719
1135
|
json: { type: 'boolean', description: 'Machine-readable output mode' },
|
|
720
1136
|
'verbose-json': { type: 'boolean', description: 'Include embeddings in JSON output (implies --json)' },
|
|
721
1137
|
pretty: { type: 'boolean', description: 'Human-readable output (default)' },
|
|
@@ -730,19 +1146,21 @@ const initOpenCodeCmd = init.sub('opencode')
|
|
|
730
1146
|
})
|
|
731
1147
|
|
|
732
1148
|
const configPath = flags.config?.trim() || '~/.suemo/suemo.ts'
|
|
1149
|
+
const dryRun = Boolean(flags['dry-run'])
|
|
733
1150
|
const npmVersion = packageJson.version ?? '0.0.0'
|
|
1151
|
+
const result = initOpenCodeFiles(configPath, dryRun)
|
|
734
1152
|
|
|
735
1153
|
if (outputMode === 'json') {
|
|
736
1154
|
console.log(JSON.stringify(
|
|
737
1155
|
{
|
|
738
1156
|
opencode: {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1157
|
+
configPath: result.configPath,
|
|
1158
|
+
configFormat: result.configFormat,
|
|
1159
|
+
configStatus: result.configStatus,
|
|
1160
|
+
configMode: result.configMode,
|
|
1161
|
+
agentsPath: result.agentsPath,
|
|
1162
|
+
agentsStatus: result.agentsStatus,
|
|
1163
|
+
dryRun: result.dryRun,
|
|
746
1164
|
},
|
|
747
1165
|
suemoVersion: npmVersion,
|
|
748
1166
|
},
|
|
@@ -752,26 +1170,12 @@ const initOpenCodeCmd = init.sub('opencode')
|
|
|
752
1170
|
return
|
|
753
1171
|
}
|
|
754
1172
|
|
|
755
|
-
console.log(
|
|
756
|
-
console.log(
|
|
757
|
-
console.log(
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
console.log(' }')
|
|
762
|
-
console.log(' }')
|
|
763
|
-
console.log('}')
|
|
764
|
-
console.log('\nMinimal AGENTS.md snippet:')
|
|
765
|
-
console.log('```md')
|
|
766
|
-
console.log('Before starting significant work:')
|
|
767
|
-
console.log('- query("what do I know about <topic>")')
|
|
768
|
-
console.log('- suemo skill (or suemo skill core-workflow) when you need latest workflow/docs')
|
|
769
|
-
console.log('During work:')
|
|
770
|
-
console.log('- observe("...") / believe("...")')
|
|
771
|
-
console.log('After completing work:')
|
|
772
|
-
console.log('- episode_end(session_id, summary="...")')
|
|
773
|
-
console.log('```')
|
|
774
|
-
console.log('\nAGENTS.md guidance source: data/AGENTS.md')
|
|
1173
|
+
console.log(`OpenCode config: ${result.configPath} (${result.configFormat})`)
|
|
1174
|
+
console.log(`MCP entry: ${result.configStatus} (${result.configMode})`)
|
|
1175
|
+
console.log(`AGENTS.md: ${result.agentsStatus} at ${result.agentsPath}`)
|
|
1176
|
+
if (dryRun) {
|
|
1177
|
+
console.log('Dry-run mode: no files were written.')
|
|
1178
|
+
}
|
|
775
1179
|
console.log(`Installed suemo version: ${npmVersion}`)
|
|
776
1180
|
})
|
|
777
1181
|
|
|
@@ -794,8 +1198,15 @@ const initSurrealCmd = init.sub('surreal')
|
|
|
794
1198
|
quiet: flags.quiet,
|
|
795
1199
|
})
|
|
796
1200
|
requireRootForInit('init surreal <2gb|6gb>')
|
|
797
|
-
|
|
798
|
-
|
|
1201
|
+
const manager = detectServiceManager()
|
|
1202
|
+
|
|
1203
|
+
requireCommands([
|
|
1204
|
+
'pacman',
|
|
1205
|
+
'install',
|
|
1206
|
+
'chown',
|
|
1207
|
+
'id',
|
|
1208
|
+
...(manager.kind === 'systemd' ? ['systemctl'] : ['ln', 'chpst']),
|
|
1209
|
+
])
|
|
799
1210
|
requireArchPackages(['surrealdb'])
|
|
800
1211
|
|
|
801
1212
|
const profileRaw = (args as { profile: string }).profile.trim().toLowerCase()
|
|
@@ -818,23 +1229,28 @@ const initSurrealCmd = init.sub('surreal')
|
|
|
818
1229
|
|
|
819
1230
|
if (dryRun) {
|
|
820
1231
|
if (outputMode === 'json') {
|
|
1232
|
+
const dryRunResult = buildSurrealActions(profile, existingPassword, force, manager)
|
|
821
1233
|
printCliJson({
|
|
822
1234
|
ok: true,
|
|
823
1235
|
dryRun: true,
|
|
824
1236
|
profile,
|
|
1237
|
+
serviceManager: manager.kind,
|
|
1238
|
+
service: dryRunResult.serviceName,
|
|
825
1239
|
existingPassword: existingPassword ?? 'none (not set)',
|
|
826
|
-
actions:
|
|
1240
|
+
actions: dryRunResult.actions,
|
|
827
1241
|
}, flags)
|
|
828
1242
|
return
|
|
829
1243
|
}
|
|
830
|
-
|
|
1244
|
+
const dryRunResult = buildSurrealActions(profile, existingPassword, force, manager)
|
|
1245
|
+
printDryRunActions(dryRunResult.actions)
|
|
1246
|
+
printInitSystemSummary('surreal', true, manager, dryRunResult.serviceName)
|
|
831
1247
|
console.log(`Existing SURREAL_PASS preview: ${existingPassword ?? 'none (not set)'}`)
|
|
832
1248
|
return
|
|
833
1249
|
}
|
|
834
1250
|
|
|
835
|
-
const result = buildSurrealActions(profile, existingPassword, force)
|
|
1251
|
+
const result = buildSurrealActions(profile, existingPassword, force, manager)
|
|
836
1252
|
applyActions(result.actions)
|
|
837
|
-
printInitSystemSummary('surreal', false,
|
|
1253
|
+
printInitSystemSummary('surreal', false, manager, result.serviceName)
|
|
838
1254
|
|
|
839
1255
|
if (result.credentialStatus === 'preserved') {
|
|
840
1256
|
console.log(
|
|
@@ -849,7 +1265,8 @@ const initSurrealCmd = init.sub('surreal')
|
|
|
849
1265
|
ok: true,
|
|
850
1266
|
dryRun: false,
|
|
851
1267
|
profile,
|
|
852
|
-
|
|
1268
|
+
serviceManager: manager.kind,
|
|
1269
|
+
service: result.serviceName,
|
|
853
1270
|
credentialStatus: result.credentialStatus,
|
|
854
1271
|
}, flags)
|
|
855
1272
|
return
|
|
@@ -873,29 +1290,42 @@ const initFastembedCmd = init.sub('fastembed')
|
|
|
873
1290
|
quiet: flags.quiet,
|
|
874
1291
|
})
|
|
875
1292
|
requireRootForInit('init fastembed')
|
|
1293
|
+
const manager = detectServiceManager()
|
|
876
1294
|
|
|
877
|
-
requireCommands([
|
|
878
|
-
|
|
1295
|
+
requireCommands([
|
|
1296
|
+
'pacman',
|
|
1297
|
+
'install',
|
|
1298
|
+
'chown',
|
|
1299
|
+
'id',
|
|
1300
|
+
...(manager.kind === 'systemd' ? ['systemctl'] : ['ln', 'chpst']),
|
|
1301
|
+
])
|
|
1302
|
+
requireArchPackages(['python-fastembed', 'python-fastapi', 'python-uvicorn'])
|
|
879
1303
|
|
|
880
1304
|
const scriptContent = FASTEMBED_SCRIPT_TEXT
|
|
881
|
-
const
|
|
1305
|
+
const result = buildFastembedActions(scriptContent, manager)
|
|
882
1306
|
const dryRun = Boolean(flags['dry-run'])
|
|
883
1307
|
|
|
884
1308
|
if (dryRun) {
|
|
885
1309
|
if (outputMode === 'json') {
|
|
886
|
-
printCliJson({
|
|
1310
|
+
printCliJson({
|
|
1311
|
+
ok: true,
|
|
1312
|
+
dryRun: true,
|
|
1313
|
+
serviceManager: manager.kind,
|
|
1314
|
+
service: result.serviceName,
|
|
1315
|
+
actions: result.actions,
|
|
1316
|
+
}, flags)
|
|
887
1317
|
return
|
|
888
1318
|
}
|
|
889
|
-
printDryRunActions(actions)
|
|
890
|
-
printInitSystemSummary('fastembed', true)
|
|
1319
|
+
printDryRunActions(result.actions)
|
|
1320
|
+
printInitSystemSummary('fastembed', true, manager, result.serviceName)
|
|
891
1321
|
return
|
|
892
1322
|
}
|
|
893
1323
|
|
|
894
|
-
applyActions(actions)
|
|
895
|
-
printInitSystemSummary('fastembed', false)
|
|
1324
|
+
applyActions(result.actions)
|
|
1325
|
+
printInitSystemSummary('fastembed', false, manager, result.serviceName)
|
|
896
1326
|
|
|
897
1327
|
if (outputMode === 'json') {
|
|
898
|
-
printCliJson({ ok: true, dryRun: false,
|
|
1328
|
+
printCliJson({ ok: true, dryRun: false, serviceManager: manager.kind, service: result.serviceName }, flags)
|
|
899
1329
|
return
|
|
900
1330
|
}
|
|
901
1331
|
})
|
|
@@ -922,7 +1352,7 @@ export const initCmd = init
|
|
|
922
1352
|
console.log('Use one of:')
|
|
923
1353
|
console.log(' suemo init config [--force]')
|
|
924
1354
|
console.log(' suemo init schema [--yes]')
|
|
925
|
-
console.log(' suemo init opencode')
|
|
1355
|
+
console.log(' suemo init opencode [--dry-run]')
|
|
926
1356
|
console.log(' suemo init surreal <2gb|6gb> [--force] [--dry-run]')
|
|
927
1357
|
console.log(' suemo init fastembed [--dry-run]')
|
|
928
1358
|
console.log(' suemo skill [reference]')
|