martty 0.3.2-beta.1 → 0.3.2
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/lib/harness-registry.js +7 -1
- package/lib/harness-view.js +10 -1
- package/lib/harnesses.js +64 -34
- package/package.json +8 -7
package/lib/harness-registry.js
CHANGED
|
@@ -20,12 +20,17 @@ import { downloadFile } from './download.js'
|
|
|
20
20
|
export const ACP_REGISTRY_URL = 'https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json'
|
|
21
21
|
|
|
22
22
|
export const OPENMA_PI_ACP = '@openma/pi-acp@latest'
|
|
23
|
+
export const OPENMA_PI_ACP_REPOSITORY = 'https://github.com/openma-ai/pi-acp'
|
|
23
24
|
|
|
24
25
|
function preferMarttyPiAcp(records) {
|
|
25
26
|
return records.map((record) => record.id === 'pi-acp' ? {
|
|
26
27
|
...record,
|
|
27
|
-
label: '
|
|
28
|
+
label: 'pi-acp',
|
|
28
29
|
version: 'latest',
|
|
30
|
+
// The official catalog also lists an unrelated unprefixed pi-acp package;
|
|
31
|
+
// Martty only ever launches @openma/pi-acp, and the repository URL is how
|
|
32
|
+
// users tell the two entries apart.
|
|
33
|
+
repository: OPENMA_PI_ACP_REPOSITORY,
|
|
29
34
|
distributions: [{
|
|
30
35
|
type: 'npx', command: 'npx', args: [OPENMA_PI_ACP], env: {},
|
|
31
36
|
}],
|
|
@@ -144,6 +149,7 @@ export function normalizeAcpRegistry(value, options = {}) {
|
|
|
144
149
|
...(typeof agent.icon === 'string' ? { icon: agent.icon } : {}),
|
|
145
150
|
version: agent.version,
|
|
146
151
|
description: typeof agent.description === 'string' ? agent.description : '',
|
|
152
|
+
...(typeof agent.repository === 'string' ? { repository: agent.repository } : {}),
|
|
147
153
|
distributions,
|
|
148
154
|
}]
|
|
149
155
|
})
|
package/lib/harness-view.js
CHANGED
|
@@ -41,6 +41,15 @@ function findDescription(entry) {
|
|
|
41
41
|
return `found locally · configure · ${commandText(entry)}`
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
// Registry entries carry their repository like the catalog's name /
|
|
45
|
+
// description / repository triple; the URL distinguishes same-named
|
|
46
|
+
// harnesses (e.g. @openma/pi-acp from the unrelated registry pi-acp).
|
|
47
|
+
function describeCandidate(entry) {
|
|
48
|
+
const detail = findDescription(entry)
|
|
49
|
+
const repository = entry.registryRecord?.repository
|
|
50
|
+
return repository === undefined ? detail : `${detail} · ${repository}`
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
export function apply(ctx, options = {}) {
|
|
45
54
|
const settingsPath = options.settingsPath
|
|
46
55
|
let registryPromise
|
|
@@ -477,7 +486,7 @@ export function apply(ctx, options = {}) {
|
|
|
477
486
|
openSelect({ id: 'harness-find', title: failure ? 'Add Harness · Registry offline' : pending ? 'Add Harness · checking Registry…' : 'Add Harness', searchable: true,
|
|
478
487
|
value: entries[0]?.id ?? REFRESH,
|
|
479
488
|
options: [
|
|
480
|
-
...entries.map((entry) => ({ value: entry.id, label: entry.label, description:
|
|
489
|
+
...entries.map((entry) => ({ value: entry.id, label: entry.label, description: describeCandidate(entry),
|
|
481
490
|
...currentOption(entry),
|
|
482
491
|
group: locallyAvailable(entry) ? 'Installed / configured' : entry.status === 'Checking locally' ? 'Catalog' : 'Not downloaded' })),
|
|
483
492
|
{ value: REFRESH, label: failure ? 'Retry Registry' : 'Refresh Registry', description: failure ? errorText(failure) : 'Recheck the catalog and local programs' },
|
package/lib/harnesses.js
CHANGED
|
@@ -16,15 +16,20 @@ import {
|
|
|
16
16
|
installRegistryBinary,
|
|
17
17
|
managedBinaryPath,
|
|
18
18
|
normalizeAcpRegistry,
|
|
19
|
-
OPENMA_PI_ACP,
|
|
20
19
|
readAcpRegistrySnapshot,
|
|
21
20
|
} from './harness-registry.js'
|
|
21
|
+
import {
|
|
22
|
+
migrateLegacyAcpAgents,
|
|
23
|
+
readCustomAcpAgents,
|
|
24
|
+
removeCustomAcpAgent,
|
|
25
|
+
upsertCustomAcpAgent,
|
|
26
|
+
} from '@openma/common/acp-harnesses/custom-agents'
|
|
22
27
|
|
|
23
28
|
export { fetchAcpRegistry } from './harness-registry.js'
|
|
24
29
|
|
|
25
30
|
const HARNESS_ID = /^[a-z0-9][a-z0-9-]*$/
|
|
26
|
-
const
|
|
27
|
-
const
|
|
31
|
+
const PI_ACP_ID = 'pi-acp'
|
|
32
|
+
const PI_ACP_LEGACY_LABEL = 'Pi (OpenMA)'
|
|
28
33
|
const ANSI = Object.freeze({ reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', cyan: '\x1b[36m', green: '\x1b[32m' })
|
|
29
34
|
|
|
30
35
|
// Kept as a source-compatible empty export. The source of truth is the
|
|
@@ -366,27 +371,50 @@ function validateHarness(value) {
|
|
|
366
371
|
}
|
|
367
372
|
const isNpx = /^npx(?:\.(?:cmd|bat|exe))?$/i.test(path.win32.basename(value.command))
|
|
368
373
|
const normalizedArgs = isNpx
|
|
369
|
-
? [...args]
|
|
370
|
-
.filter((arg) => arg !== '--yes' && arg !== '--prefer-offline')
|
|
371
|
-
// A pinned pi-acp spec (any old `pi-acp@x` or `@openma/pi-acp@x` entry)
|
|
372
|
-
// always resolves `latest` at runtime instead — harness upgrades ship
|
|
373
|
-
// through npm, never through a Martty release.
|
|
374
|
-
.map((arg) => PI_ACP_SPEC.test(arg) ? PI_ACP_LATEST : arg)
|
|
374
|
+
? [...args].filter((arg) => arg !== '--yes' && arg !== '--prefer-offline')
|
|
375
375
|
: [...args]
|
|
376
|
+
const label = typeof value.label === 'string' && value.label.trim().length > 0
|
|
377
|
+
? value.label
|
|
378
|
+
: value.id
|
|
376
379
|
return {
|
|
377
380
|
id: value.id,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
+
// Configs saved when the pi harness was branded "Pi (OpenMA)" keep
|
|
382
|
+
// working; only the stale default display label migrates to the id.
|
|
383
|
+
label: value.id === PI_ACP_ID && label === PI_ACP_LEGACY_LABEL ? PI_ACP_ID : label,
|
|
381
384
|
command: value.command,
|
|
382
385
|
args: normalizedArgs,
|
|
383
386
|
...(env !== undefined ? { env: { ...env } } : {}),
|
|
384
387
|
}
|
|
385
388
|
}
|
|
386
389
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
+
// Harness definitions live in <oma>/agents.json, the shared store from
|
|
391
|
+
// @openma/common — Backchat reads and writes the same file. Legacy rows
|
|
392
|
+
// still inside settings.json (and Backchat's config.toml overrides) keep
|
|
393
|
+
// reading through the shared module; the first shared write migrates
|
|
394
|
+
// harnesses[] into config.toml and strips the key so removals stick.
|
|
395
|
+
function settingsHome(settingsPath) {
|
|
396
|
+
return path.dirname(settingsPath)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function migrateStoredLabel(agent) {
|
|
400
|
+
// Configs saved when the pi harness was branded "Pi (OpenMA)" keep
|
|
401
|
+
// working; only the stale default display label migrates to the id.
|
|
402
|
+
return agent.id === PI_ACP_ID && agent.label === PI_ACP_LEGACY_LABEL
|
|
403
|
+
? { ...agent, label: PI_ACP_ID }
|
|
404
|
+
: agent
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function configuredHarnesses(settingsPath) {
|
|
408
|
+
// enabled=false is Backchat's disable flag: the definition stays stored
|
|
409
|
+
// but a disabled agent must not be selectable or launchable here.
|
|
410
|
+
return readCustomAcpAgents(settingsHome(settingsPath))
|
|
411
|
+
.filter((agent) => agent.enabled !== false)
|
|
412
|
+
.map(migrateStoredLabel)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function settingsWithoutHarnesses(settings) {
|
|
416
|
+
const { harnesses: _legacyHarnesses, ...rest } = settings
|
|
417
|
+
return rest
|
|
390
418
|
}
|
|
391
419
|
|
|
392
420
|
function withoutLegacyActive(settings) {
|
|
@@ -409,36 +437,37 @@ function persistedDefaultId(settings) {
|
|
|
409
437
|
|
|
410
438
|
export function upsertHarness(settingsPath, harness) {
|
|
411
439
|
const next = validateHarness(harness)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
writeSettings(settingsPath,
|
|
440
|
+
// The shared write migrates legacy harnesses[] into config.toml and
|
|
441
|
+
// strips the key; read settings after it so a stale copy cannot race
|
|
442
|
+
// the migrated file back in.
|
|
443
|
+
upsertCustomAcpAgent(settingsHome(settingsPath), next)
|
|
444
|
+
const settings = withoutLegacyActive(readSettings(settingsPath))
|
|
445
|
+
writeSettings(settingsPath, settingsWithoutHarnesses(settings))
|
|
418
446
|
return next
|
|
419
447
|
}
|
|
420
448
|
|
|
421
449
|
export function setDefaultHarness(settingsPath, id) {
|
|
422
|
-
const
|
|
423
|
-
const harnesses = configuredHarnesses(settings)
|
|
450
|
+
const harnesses = configuredHarnesses(settingsPath)
|
|
424
451
|
if (!harnesses.some((harness) => harness.id === id)) {
|
|
425
452
|
throw new Error(`unknown harness ${JSON.stringify(id)}`)
|
|
426
453
|
}
|
|
427
|
-
|
|
454
|
+
const settings = withoutLegacyActive(readSettings(settingsPath))
|
|
455
|
+
migrateLegacyAcpAgents(settingsHome(settingsPath))
|
|
456
|
+
writeSettings(settingsPath, { ...settingsWithoutHarnesses(settings), defaultHarness: id })
|
|
428
457
|
}
|
|
429
458
|
|
|
430
459
|
export function savedHarnesses(settingsPath) {
|
|
431
|
-
return configuredHarnesses(
|
|
460
|
+
return configuredHarnesses(settingsPath)
|
|
432
461
|
}
|
|
433
462
|
|
|
434
463
|
export function removeHarnessConfiguration(settingsPath, expected) {
|
|
435
|
-
const
|
|
436
|
-
const harnesses = configuredHarnesses(settings)
|
|
437
|
-
const current = harnesses.find(({ id }) => id === expected.id)
|
|
464
|
+
const current = configuredHarnesses(settingsPath).find(({ id }) => id === expected.id)
|
|
438
465
|
if (JSON.stringify(current) !== JSON.stringify(expected)) throw new Error('Harness configuration changed; review removal again')
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
466
|
+
removeCustomAcpAgent(settingsHome(settingsPath), expected.id)
|
|
467
|
+
const settings = withoutLegacyActive(readSettings(settingsPath))
|
|
468
|
+
const rest = settingsWithoutHarnesses(settings)
|
|
469
|
+
if (rest.defaultHarness === expected.id) delete rest.defaultHarness
|
|
470
|
+
writeSettings(settingsPath, rest)
|
|
442
471
|
}
|
|
443
472
|
|
|
444
473
|
// Kept as a source-compatible alias for integrations compiled against 0.2.30.
|
|
@@ -449,7 +478,7 @@ export function selectedHarness(settingsPath) {
|
|
|
449
478
|
const settings = readSettings(settingsPath)
|
|
450
479
|
const id = persistedDefaultId(settings)
|
|
451
480
|
if (typeof id !== 'string') return undefined
|
|
452
|
-
return configuredHarnesses(
|
|
481
|
+
return configuredHarnesses(settingsPath).find((harness) => harness.id === id)
|
|
453
482
|
}
|
|
454
483
|
|
|
455
484
|
function executableFile(command) {
|
|
@@ -529,6 +558,7 @@ function registryRecords(options = {}) {
|
|
|
529
558
|
: record.id,
|
|
530
559
|
version: typeof record.version === 'string' ? record.version : 'unversioned',
|
|
531
560
|
description: typeof record.description === 'string' ? record.description : '',
|
|
561
|
+
...(typeof record.repository === 'string' ? { repository: record.repository } : {}),
|
|
532
562
|
distributions,
|
|
533
563
|
}]
|
|
534
564
|
}
|
|
@@ -1004,7 +1034,7 @@ export function discoverHarnesses(settingsPath, options = {}) {
|
|
|
1004
1034
|
}
|
|
1005
1035
|
|
|
1006
1036
|
function discoverHarnessEntries(settingsPath, options, registryEntries) {
|
|
1007
|
-
const configured = configuredHarnesses(
|
|
1037
|
+
const configured = configuredHarnesses(settingsPath)
|
|
1008
1038
|
.map((harness) => ({ ...harness, source: 'configured' }))
|
|
1009
1039
|
const defaults = (options.defaults ?? []).map((entry) => ({
|
|
1010
1040
|
...validateHarness(entry),
|
|
@@ -1071,7 +1101,7 @@ function searchScore(entry, query = '') {
|
|
|
1071
1101
|
}
|
|
1072
1102
|
|
|
1073
1103
|
function findHarnessCandidates(settingsPath, options = {}, query = '') {
|
|
1074
|
-
const configured = configuredHarnesses(
|
|
1104
|
+
const configured = configuredHarnesses(settingsPath).map((entry) => ({
|
|
1075
1105
|
...entry,
|
|
1076
1106
|
source: 'configured',
|
|
1077
1107
|
status: 'Configured',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "martty",
|
|
3
|
-
"version": "0.3.2
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Terminal-native ACP client UI; Cordis client tree, any ACP agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -77,7 +77,8 @@
|
|
|
77
77
|
"dependencies": {
|
|
78
78
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
79
79
|
"@lobehub/icons-static-svg": "1.95.0",
|
|
80
|
-
"@openma/
|
|
80
|
+
"@openma/common": "^0.9.2",
|
|
81
|
+
"@openma/deepseek-harness-acp": "0.4.38",
|
|
81
82
|
"@resvg/resvg-js": "2.6.2",
|
|
82
83
|
"cross-spawn": "^7.0.6",
|
|
83
84
|
"node-downloader-helper": "2.1.11"
|
|
@@ -97,10 +98,10 @@
|
|
|
97
98
|
"agent-client-protocol"
|
|
98
99
|
],
|
|
99
100
|
"optionalDependencies": {
|
|
100
|
-
"@openma/martty-darwin-arm64": "0.3.2
|
|
101
|
-
"@openma/martty-darwin-x64": "0.3.2
|
|
102
|
-
"@openma/martty-linux-x64": "0.3.2
|
|
103
|
-
"@openma/martty-linux-arm64": "0.3.2
|
|
104
|
-
"@openma/martty-win32-x64": "0.3.2
|
|
101
|
+
"@openma/martty-darwin-arm64": "0.3.2",
|
|
102
|
+
"@openma/martty-darwin-x64": "0.3.2",
|
|
103
|
+
"@openma/martty-linux-x64": "0.3.2",
|
|
104
|
+
"@openma/martty-linux-arm64": "0.3.2",
|
|
105
|
+
"@openma/martty-win32-x64": "0.3.2"
|
|
105
106
|
}
|
|
106
107
|
}
|