@2en/clawly-plugins 1.26.0-beta.1 → 1.26.0
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/config-setup.ts +108 -17
- package/model-gateway-setup.ts +5 -7
- package/package.json +1 -1
package/config-setup.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Reconciles runtime config owned by clawly-plugins after provision has already
|
|
3
|
+
* established the bootstrap baseline (plugin entry, pluginConfig inputs,
|
|
4
|
+
* workspace files, install metadata).
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* provider setup.
|
|
6
|
+
* This file intentionally owns two narrower responsibilities:
|
|
7
|
+
* - repair legacy state damaged by older provision flows
|
|
8
|
+
* - reconcile runtime-facing config derived from pluginConfig
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* It does not try to redefine provision's startup contract. Anything that must
|
|
11
|
+
* exist before the first gateway boot should remain provision-owned.
|
|
12
|
+
*
|
|
13
|
+
* Backward compatibility: old sprites may have only the original gateway
|
|
14
|
+
* credentials in pluginConfig. Helpers check for newer fields and skip
|
|
15
|
+
* gracefully when absent.
|
|
13
16
|
*/
|
|
14
17
|
|
|
18
|
+
import fs from 'node:fs'
|
|
15
19
|
import path from 'node:path'
|
|
16
20
|
|
|
17
21
|
import type {PluginApi} from './index'
|
|
@@ -377,8 +381,100 @@ export function patchSession(config: Record<string, unknown>): boolean {
|
|
|
377
381
|
return dirty
|
|
378
382
|
}
|
|
379
383
|
|
|
384
|
+
const PLUGIN_ID = 'clawly-plugins'
|
|
385
|
+
const NPM_PKG_NAME = '@2en/clawly-plugins'
|
|
386
|
+
|
|
387
|
+
function asObj(value: unknown): Record<string, unknown> {
|
|
388
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
389
|
+
? (value as Record<string, unknown>)
|
|
390
|
+
: {}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Self-healing: reconstruct missing `plugins.installs.clawly-plugins` record.
|
|
395
|
+
* Older provisioned sprites had this record destroyed by a full-overwrite bug
|
|
396
|
+
* in the provision write-plugins-config step. Without this record,
|
|
397
|
+
* `openclaw plugins update` cannot function.
|
|
398
|
+
*/
|
|
399
|
+
export function patchInstallRecord(config: Record<string, unknown>, stateDir: string): boolean {
|
|
400
|
+
const plugins = asObj(config.plugins)
|
|
401
|
+
const installs = asObj(plugins.installs)
|
|
402
|
+
|
|
403
|
+
// Already has a valid install record — nothing to do
|
|
404
|
+
if (
|
|
405
|
+
installs[PLUGIN_ID] &&
|
|
406
|
+
typeof installs[PLUGIN_ID] === 'object' &&
|
|
407
|
+
(installs[PLUGIN_ID] as Record<string, unknown>).source &&
|
|
408
|
+
(installs[PLUGIN_ID] as Record<string, unknown>).spec
|
|
409
|
+
)
|
|
410
|
+
return false
|
|
411
|
+
|
|
412
|
+
// Read version from installed plugin's package.json
|
|
413
|
+
const installPath = path.join(stateDir, 'extensions', PLUGIN_ID)
|
|
414
|
+
const pkgPath = path.join(installPath, 'package.json')
|
|
415
|
+
let rawPkg: string
|
|
416
|
+
try {
|
|
417
|
+
rawPkg = fs.readFileSync(pkgPath, 'utf-8')
|
|
418
|
+
} catch {
|
|
419
|
+
// Plugin not installed on disk — skip
|
|
420
|
+
return false
|
|
421
|
+
}
|
|
422
|
+
let version: string | undefined
|
|
423
|
+
try {
|
|
424
|
+
const pkg = JSON.parse(rawPkg)
|
|
425
|
+
if (typeof pkg.version === 'string') version = pkg.version
|
|
426
|
+
} catch {
|
|
427
|
+
// package.json is malformed — proceed without version
|
|
428
|
+
}
|
|
429
|
+
let installedAt = new Date().toISOString()
|
|
430
|
+
try {
|
|
431
|
+
installedAt = fs.statSync(pkgPath).mtime.toISOString()
|
|
432
|
+
} catch {
|
|
433
|
+
// Fall back to repair time if file metadata is unavailable.
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
installs[PLUGIN_ID] = {
|
|
437
|
+
source: 'npm',
|
|
438
|
+
spec: version ? `${NPM_PKG_NAME}@${version}` : NPM_PKG_NAME,
|
|
439
|
+
installPath,
|
|
440
|
+
version,
|
|
441
|
+
installedAt,
|
|
442
|
+
}
|
|
443
|
+
plugins.installs = installs
|
|
444
|
+
config.plugins = plugins
|
|
445
|
+
return true
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function repairLegacyProvisionState(
|
|
449
|
+
api: PluginApi,
|
|
450
|
+
config: Record<string, unknown>,
|
|
451
|
+
stateDir: string,
|
|
452
|
+
) {
|
|
453
|
+
const installRecordPatched = patchInstallRecord(config, stateDir)
|
|
454
|
+
if (installRecordPatched) {
|
|
455
|
+
api.logger.warn('plugins.installs.clawly-plugins was missing — self-healed from disk.')
|
|
456
|
+
}
|
|
457
|
+
return installRecordPatched
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function reconcileRuntimeConfig(
|
|
461
|
+
api: PluginApi,
|
|
462
|
+
config: Record<string, unknown>,
|
|
463
|
+
pc: ConfigPluginConfig,
|
|
464
|
+
): boolean {
|
|
465
|
+
let dirty = false
|
|
466
|
+
dirty = patchAgent(config, pc) || dirty
|
|
467
|
+
dirty = patchGateway(config) || dirty
|
|
468
|
+
dirty = patchBrowser(config) || dirty
|
|
469
|
+
dirty = patchSession(config) || dirty
|
|
470
|
+
dirty = patchTts(config, pc) || dirty
|
|
471
|
+
dirty = patchWebSearch(config, pc) || dirty
|
|
472
|
+
dirty = patchModelGateway(config, api) || dirty
|
|
473
|
+
return dirty
|
|
474
|
+
}
|
|
475
|
+
|
|
380
476
|
// ---------------------------------------------------------------------------
|
|
381
|
-
// Entry point — single read,
|
|
477
|
+
// Entry point — single read, targeted reconcile, single write
|
|
382
478
|
// ---------------------------------------------------------------------------
|
|
383
479
|
|
|
384
480
|
export function setupConfig(api: PluginApi): void {
|
|
@@ -393,13 +489,8 @@ export function setupConfig(api: PluginApi): void {
|
|
|
393
489
|
const pc = toPC(api)
|
|
394
490
|
|
|
395
491
|
let dirty = false
|
|
396
|
-
dirty =
|
|
397
|
-
dirty =
|
|
398
|
-
dirty = patchBrowser(config) || dirty
|
|
399
|
-
dirty = patchSession(config) || dirty
|
|
400
|
-
dirty = patchTts(config, pc) || dirty
|
|
401
|
-
dirty = patchWebSearch(config, pc) || dirty
|
|
402
|
-
dirty = patchModelGateway(config, api) || dirty
|
|
492
|
+
dirty = repairLegacyProvisionState(api, config, stateDir) || dirty
|
|
493
|
+
dirty = reconcileRuntimeConfig(api, config, pc) || dirty
|
|
403
494
|
|
|
404
495
|
if (!dirty) {
|
|
405
496
|
api.logger.info('Config setup: no changes needed.')
|
package/model-gateway-setup.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* is derived from `agents.defaults.model`
|
|
5
|
-
* already present in the config.
|
|
2
|
+
* Reconciles the `clawly-model-gateway` provider in openclaw.json from
|
|
3
|
+
* pluginConfig inputs and the current runtime-facing agent defaults.
|
|
6
4
|
*
|
|
7
|
-
* This
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* This is a runtime reconcile path, not a provision/bootstrap contract.
|
|
6
|
+
* The first-boot correctness of gateway startup should not rely on this file
|
|
7
|
+
* write winning a race against OpenClaw's internal startup snapshot timing.
|
|
10
8
|
*/
|
|
11
9
|
|
|
12
10
|
import fs from 'node:fs'
|