@jamaynor/hal-config 1.0.2 → 1.1.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/index.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './lib/config.js';
2
+ export * from './lib/types.js';
3
+ import * as config from './lib/config.js';
4
+ export default config;
@@ -0,0 +1,111 @@
1
+ import type {
2
+ HalCommunicationAccountEntry,
3
+ HalRuntimeContext,
4
+ HalVaultAreas,
5
+ } from './types.js';
6
+
7
+ export interface ResolveWorkspaceOptions {
8
+ workspace?: string;
9
+ envPrefix?: string;
10
+ fallback?: string | null;
11
+ }
12
+
13
+ export interface RuntimeContextOptions {
14
+ callingAgentRuntimePath?: string | null;
15
+ }
16
+
17
+ export interface SkillRuntimeData {
18
+ [key: string]: unknown;
19
+ }
20
+
21
+ export interface SkillInstallData {
22
+ binary?: string | string[];
23
+ [key: string]: unknown;
24
+ }
25
+
26
+ export interface SkillEntry {
27
+ enabled?: boolean;
28
+ homeAgent?: string | null;
29
+ runtime?: SkillRuntimeData;
30
+ install?: SkillInstallData;
31
+ [key: string]: unknown;
32
+ }
33
+
34
+ export interface HalSystemConfig {
35
+ skills?: Record<string, SkillEntry>;
36
+ 'hal-obsidian-vaults'?: Array<Record<string, unknown>>;
37
+ 'hal-communication-accounts'?: HalCommunicationAccountEntry[];
38
+ 'hal-timezone'?: string;
39
+ 'hal-work-hours'?: { start: string; end: string } | null;
40
+ [key: string]: unknown;
41
+ }
42
+
43
+ export interface VaultDirectoryPaths {
44
+ vaultPath: string | null;
45
+ projectsPath: string | null;
46
+ dailyNotesPath: string | null;
47
+ emailPath: string | null;
48
+ meetingsPath: string | null;
49
+ peoplePath: string | null;
50
+ }
51
+
52
+ export interface HalSkillConfigApi {
53
+ normalizeSkillName(skillScope: string): string;
54
+ adminRoot(skillName: string): string;
55
+ configDir(skillName: string): string;
56
+ logDir(skillName: string): string;
57
+ configPath(skillName: string): string;
58
+ read(skillScope: string): Record<string, unknown>;
59
+ getSetting(skillScope: string, key: string): unknown;
60
+ write(skillScope: string, data: Record<string, unknown>): void;
61
+ merge(skillScope: string, patch: Record<string, unknown>): Record<string, unknown>;
62
+ setSetting(skillScope: string, key: string, value: unknown): Record<string, unknown>;
63
+ }
64
+
65
+ export declare function load(configDir?: string): HalSystemConfig;
66
+ export declare function reload(): HalSystemConfig;
67
+ export declare function raw(): HalSystemConfig;
68
+ export declare function configDir(): string;
69
+
70
+ export declare function openclawSharedWorkspaceRoot(): string;
71
+ export declare function openclawConfigFilePath(): string;
72
+ export declare function halSystemConfigDir(): string;
73
+ export declare function halSystemConfigFilePath(): string;
74
+ export declare function halSkillAdminRoot(skillName: string): string;
75
+ export declare function halSkillConfigDir(skillName: string): string;
76
+ export declare function halSkillLogDir(skillName: string): string;
77
+
78
+ export declare const halSkillConfig: HalSkillConfigApi;
79
+ export declare function skillConfigPath(name: string): string;
80
+ export declare function skillConfigDir(name: string): string;
81
+
82
+ export declare function vaults(): Array<Record<string, unknown>>;
83
+ export declare function vaultFolders(vaultName: string): HalVaultAreas;
84
+ export declare function vaultDirectoryPaths(vaultName: string): VaultDirectoryPaths;
85
+ export declare function accounts(provider?: string): HalCommunicationAccountEntry[];
86
+ export declare function timezone(): string | null;
87
+ export declare function workHours(): { start: string; end: string } | null;
88
+
89
+ export declare function getSetting(scopeOrKey: string, key?: string): unknown;
90
+ export declare const GetSetting: typeof getSetting;
91
+
92
+ export declare function skill(name: string): SkillEntry | null;
93
+ export declare function isEnabled(name: string): boolean;
94
+ export declare function skillWorkspace(name: string): string | null;
95
+ export declare function skillBinaries(name: string): string[];
96
+ export declare function homeAgent(name: string): string | null;
97
+ export declare function isInstalled(name: string): boolean;
98
+ export declare function isInstalledAsync(name: string): Promise<boolean>;
99
+
100
+ export declare function getRuntimeContext(opts?: RuntimeContextOptions): HalRuntimeContext | { general: HalRuntimeContext['general'] };
101
+
102
+ export declare function register(name: string, runtimeData: Record<string, unknown>): void;
103
+ export declare function upsertCommunicationAccount(account: HalCommunicationAccountEntry): void;
104
+ export declare function deleteCommunicationAccount(label: string): boolean;
105
+ export declare function writeSkillConfig(name: string, data: Record<string, unknown>): void;
106
+ export declare function writeSharedSettings(settings: Record<string, unknown>): void;
107
+
108
+ export declare function resolveWorkspace(opts?: ResolveWorkspaceOptions): string;
109
+ export declare function getSkillConfig(skillName: string, workspace: string): Record<string, unknown>;
110
+ export declare const loadSkillConfig: typeof getSkillConfig;
111
+ export declare function saveSkillConfig(skillName: string, workspace: string, data: Record<string, unknown>): void;
package/lib/config.js CHANGED
@@ -1,11 +1,37 @@
1
1
  // =============================================================================
2
- // hal-shared-config — Shared configuration loader for HAL OpenClaw skills
2
+ // hal-config/lib/config
3
3
  //
4
- // Reads system-config.json once, caches for the process lifetime, and exposes
5
- // accessors for shared data (vaults, accounts, timezone, etc.) and skill
6
- // discovery (enabled, installed, workspace, binaries).
4
+ // SRP (Single Responsibility)
5
+ // - Load + cache `hal-system-config.json` and provide a single, stable API for
6
+ // HAL/OpenClaw skills to read shared settings (vaults, accounts, timezone,
7
+ // work hours), resolve workspaces, and locate per-skill config/log directories.
7
8
  //
8
- // Skills use this instead of each maintaining their own system-config reader.
9
+ // Public Interface
10
+ // config
11
+ // ├── Loading / cache
12
+ // │ ├── load(configDir?)
13
+ // │ ├── reload()
14
+ // │ └── configDir()
15
+ // ├── Runtime context (primary read API)
16
+ // │ ├── getRuntimeContext(opts?) -> HalRuntimeContext
17
+ // │ │ opts.callingAgentRuntimePath?: string|null
18
+ // │ │ - If null/unresolved, returns only `general` (no `callingAgent`)
19
+ // │ └── HalRuntimeContext
20
+ // │ ├── general
21
+ // │ └── callingAgent? (optional)
22
+ // │ └── skills[*].agentHasAccess (resolved from homeAgent + caller)
23
+ // ├── Installation checks
24
+ // │ ├── isInstalled(skillName)
25
+ // │ └── isInstalledAsync(skillName)
26
+ // ├── Per-skill config I/O (skill-owned files; separate from hal-system-config.json)
27
+ // │ ├── getSkillConfig(skillName, workspace)
28
+ // │ └── saveSkillConfig(skillName, workspace, data)
29
+ // └── Init-wizard writes (mutate hal-system-config.json)
30
+ // ├── register(name, runtimeData)
31
+ // ├── upsertCommunicationAccount(account)
32
+ // ├── deleteCommunicationAccount(label)
33
+ // ├── writeSkillConfig(name, data)
34
+ // └── writeSharedSettings(settings)
9
35
  // =============================================================================
10
36
 
11
37
  import fs from 'node:fs';
@@ -474,6 +500,190 @@ export function isInstalledAsync(name) {
474
500
  return _isBinaryOnPathAsync(bins[0]);
475
501
  }
476
502
 
503
+ function _canWriteDir(dirPath) {
504
+ try {
505
+ if (!dirPath) return false;
506
+ fs.accessSync(dirPath, fs.constants.W_OK);
507
+ return true;
508
+ } catch {
509
+ return false;
510
+ }
511
+ }
512
+
513
+ function _deriveAgentIdFromRuntimePath(runtimePath) {
514
+ const normalized = path.resolve(String(runtimePath || '').trim());
515
+ if (!normalized) return null;
516
+ const id = path.basename(normalized);
517
+ return id || null;
518
+ }
519
+
520
+ function _vaultDirectoryName(vaultPath) {
521
+ const p = String(vaultPath || '').trim();
522
+ if (!p) return '';
523
+ const normalized = p.replace(/[\\/]+$/, '');
524
+ const useWin = normalized.includes('\\') || /^[a-zA-Z]:\\/.test(normalized);
525
+ return useWin ? path.win32.basename(normalized) : path.posix.basename(normalized);
526
+ }
527
+
528
+ function _resolveMasterVault(vaultEntries) {
529
+ const masters = vaultEntries.filter(v => v.role === 'master');
530
+ if (masters.length === 0) return null;
531
+ if (masters.length > 1) {
532
+ throw new Error('ERROR: Multiple master vaults configured in hal-obsidian-vaults.');
533
+ }
534
+ const m = masters[0];
535
+ return {
536
+ name: m.name,
537
+ label: m.label,
538
+ absolutePath: m.absolutePath,
539
+ directoryName: m.directoryName,
540
+ };
541
+ }
542
+
543
+ /**
544
+ * Build a normalized runtime context for HAL/OpenClaw consumers.
545
+ *
546
+ * @param {{ callingAgentRuntimePath?: string|null }} [opts]
547
+ * @returns {{ general: object, callingAgent?: object }}
548
+ */
549
+ export function getRuntimeContext(opts) {
550
+ _ensure();
551
+ const o = opts || {};
552
+
553
+ const openClawRoot = '/data/openclaw';
554
+ const openClawAgentRoot = '/data/agents';
555
+ const openClawConfigFile = path.join(openClawRoot, 'openclaw.json');
556
+ const openClawSharedAgentWorkspace = openclawSharedWorkspaceRoot();
557
+ const halRoot = halSystemConfigDir();
558
+ const halSystemConfigFile = halSystemConfigFilePath();
559
+
560
+ const rawVaults = vaults();
561
+ const vaultEntries = rawVaults.map(v => {
562
+ const name = String(v?.name || '').trim();
563
+ const label = String(v?.label || name).trim();
564
+ const absolutePath = String(v?.path || '').trim();
565
+ const role = String(v?.role || v?.vaultType || 'standard').trim().toLowerCase() === 'master'
566
+ ? 'master'
567
+ : 'standard';
568
+ const folderNames = vaultFolders(name);
569
+ return {
570
+ name,
571
+ label,
572
+ role,
573
+ absolutePath,
574
+ directoryName: _vaultDirectoryName(absolutePath),
575
+ knownAreas: {
576
+ projectsPath: _joinVaultSubpath(absolutePath, folderNames.projectsFolder),
577
+ dailyNotesPath: _joinVaultSubpath(absolutePath, folderNames.dailyNotesFolder),
578
+ emailPath: _joinVaultSubpath(absolutePath, folderNames.emailFolder),
579
+ meetingsPath: _joinVaultSubpath(absolutePath, folderNames.meetingsFolder),
580
+ peoplePath: _joinVaultSubpath(absolutePath, folderNames.peopleFolder),
581
+ },
582
+ };
583
+ });
584
+
585
+ const skillMap = _cache.skills || {};
586
+ const skills = {};
587
+ for (const [skillName, skillData] of Object.entries(skillMap)) {
588
+ const bins = (() => {
589
+ const bin = skillData?.install?.binary;
590
+ if (!bin) return [];
591
+ return Array.isArray(bin) ? bin : [bin];
592
+ })();
593
+ skills[skillName] = {
594
+ enabled: skillData?.enabled !== false,
595
+ homeAgent: skillData?.homeAgent || null,
596
+ binaries: bins,
597
+ configPath: skillConfigPath(skillName),
598
+ configDir: skillConfigDir(skillName),
599
+ };
600
+ }
601
+
602
+ const general = {
603
+ openClawPaths: {
604
+ openClawRoot,
605
+ openClawAgentRoot,
606
+ openClawConfigFile,
607
+ openClawSharedAgentWorkspace,
608
+ },
609
+ halPaths: {
610
+ halRoot,
611
+ halSystemConfigDir: halRoot,
612
+ halSystemConfigFile,
613
+ },
614
+ masterTemplates: {
615
+ masterUserPath: path.join(openClawSharedAgentWorkspace, 'USER.md'),
616
+ masterSafetyPath: path.join(openClawSharedAgentWorkspace, 'SAFETY.md'),
617
+ },
618
+ vaults: vaultEntries,
619
+ masterVault: _resolveMasterVault(vaultEntries),
620
+ communicationAccounts: _cache['hal-communication-accounts'] || [],
621
+ settings: {
622
+ timezone: _cache['hal-timezone'] || null,
623
+ workHours: _cache['hal-work-hours'] || null,
624
+ },
625
+ skills,
626
+ };
627
+
628
+ const runtimePathRaw = (o.callingAgentRuntimePath ?? process.env.HAL_AGENT_WORKSPACE ?? '').toString().trim();
629
+ if (!runtimePathRaw) return { general };
630
+
631
+ const runtimePath = path.resolve(runtimePathRaw);
632
+ const agentId = _deriveAgentIdFromRuntimePath(runtimePath);
633
+
634
+ const skillPathsBySkill = {};
635
+ const accessBySkill = {};
636
+ const agentSkillState = {};
637
+ for (const [skillName, skillData] of Object.entries(skills)) {
638
+ const sharedConfigDir = halSkillConfigDir(skillName);
639
+ const sharedLogDir = halSkillLogDir(skillName);
640
+ const agentWorkingDir = path.join(runtimePath, 'hal', skillName);
641
+ skillPathsBySkill[skillName] = {
642
+ skillName,
643
+ sharedConfigDir,
644
+ sharedLogDir,
645
+ agentWorkingDir,
646
+ agentStateDir: path.join(agentWorkingDir, 'state'),
647
+ agentCacheDir: path.join(agentWorkingDir, 'cache'),
648
+ agentTmpDir: path.join(agentWorkingDir, 'tmp'),
649
+ agentSweepDir: path.join(agentWorkingDir, 'sweep'),
650
+ };
651
+ const hasAccess = !skillData.homeAgent || (agentId && skillData.homeAgent === agentId);
652
+ accessBySkill[skillName] = !!hasAccess;
653
+ agentSkillState[skillName] = { agentHasAccess: !!hasAccess };
654
+ }
655
+
656
+ const callingAgent = {
657
+ id: agentId,
658
+ workspacePath: runtimePath,
659
+ identityFiles: {
660
+ soul: path.join(runtimePath, 'SOUL.md'),
661
+ agents: path.join(runtimePath, 'AGENTS.md'),
662
+ identity: path.join(runtimePath, 'IDENTITY.md'),
663
+ heartbeat: path.join(runtimePath, 'HEARTBEAT.md'),
664
+ tools: path.join(runtimePath, 'TOOLS.md'),
665
+ user: path.join(runtimePath, 'USER.md'),
666
+ safety: path.join(runtimePath, 'SAFETY.md'),
667
+ },
668
+ stores: {
669
+ memoryDir: path.join(runtimePath, 'memory'),
670
+ knowledgeDir: path.join(runtimePath, 'knowledge'),
671
+ skillsDir: path.join(runtimePath, 'skills'),
672
+ },
673
+ skillPaths: {
674
+ bySkill: skillPathsBySkill,
675
+ },
676
+ skills: agentSkillState,
677
+ capabilities: {
678
+ canWriteAgentWorkspace: _canWriteDir(runtimePath),
679
+ canWriteSharedWorkspace: _canWriteDir(openClawSharedAgentWorkspace),
680
+ isHomeAgentForSkill: accessBySkill,
681
+ },
682
+ };
683
+
684
+ return { general, callingAgent };
685
+ }
686
+
477
687
  // ---------------------------------------------------------------------------
478
688
  // Binary helpers
479
689
  // ---------------------------------------------------------------------------
@@ -521,25 +731,51 @@ export function register(name, runtimeData) {
521
731
  }
522
732
 
523
733
  /**
524
- * Merge communication accounts by label (upsert — match by label, replace or
525
- * append). Never deletes entries whose label isn't in newAccounts.
526
- * @param {Array} newAccounts — accounts to upsert
734
+ * Upsert a single communication account by label.
735
+ *
736
+ * @param {import('./types').HalCommunicationAccountEntry} account
527
737
  */
528
- export function writeAccounts(newAccounts) {
738
+ export function upsertCommunicationAccount(account) {
529
739
  _ensure();
530
- const existing = _cache['hal-communication-accounts'] || [];
531
- for (const acc of newAccounts) {
532
- const idx = existing.findIndex(e => e.label === acc.label);
533
- if (idx >= 0) {
534
- existing[idx] = acc;
535
- } else {
536
- existing.push(acc);
537
- }
740
+ if (!account || typeof account !== 'object') {
741
+ throw new Error('ERROR: upsertCommunicationAccount requires an account object.');
742
+ }
743
+ if (!account.label || typeof account.label !== 'string') {
744
+ throw new Error('ERROR: upsertCommunicationAccount requires account.label (string).');
538
745
  }
746
+ if (!account.provider || typeof account.provider !== 'string') {
747
+ throw new Error('ERROR: upsertCommunicationAccount requires account.provider (string).');
748
+ }
749
+
750
+ const existing = _cache['hal-communication-accounts'] || [];
751
+ const idx = existing.findIndex(e => e.label === account.label);
752
+ if (idx >= 0) existing[idx] = account;
753
+ else existing.push(account);
754
+
539
755
  _cache['hal-communication-accounts'] = existing;
540
756
  _flush();
541
757
  }
542
758
 
759
+ /**
760
+ * Delete a communication account by label.
761
+ *
762
+ * @param {string} label
763
+ * @returns {boolean} true when an entry was removed
764
+ */
765
+ export function deleteCommunicationAccount(label) {
766
+ _ensure();
767
+ if (!label || typeof label !== 'string') {
768
+ throw new Error('ERROR: deleteCommunicationAccount requires label (string).');
769
+ }
770
+
771
+ const existing = _cache['hal-communication-accounts'] || [];
772
+ const before = existing.length;
773
+ _cache['hal-communication-accounts'] = existing.filter(e => e?.label !== label);
774
+ const removed = _cache['hal-communication-accounts'].length < before;
775
+ if (removed) _flush();
776
+ return removed;
777
+ }
778
+
543
779
  /**
544
780
  * Merge key/value pairs into a skill's config block (not runtime — top level
545
781
  * of the skill entry). Used by init wizards to store skill-specific settings.
@@ -623,7 +859,7 @@ export function resolveWorkspace(opts) {
623
859
  * @param {string} workspace
624
860
  * @returns {object}
625
861
  */
626
- export function loadSkillConfig(skillName, workspace) {
862
+ export function getSkillConfig(skillName, workspace) {
627
863
  const filePath = path.join(workspace, 'hal', 'config', `${skillName}.json`);
628
864
  try {
629
865
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
@@ -633,6 +869,9 @@ export function loadSkillConfig(skillName, workspace) {
633
869
  }
634
870
  }
635
871
 
872
+ // Backward compatibility alias; prefer getSkillConfig().
873
+ export const loadSkillConfig = getSkillConfig;
874
+
636
875
  /**
637
876
  * Deep-merge source into target. Plain objects are merged recursively.
638
877
  * Arrays are replaced atomically. Non-object values overwrite.
package/lib/types.d.ts ADDED
@@ -0,0 +1,218 @@
1
+ /**
2
+ * =============================================================================
3
+ * hal-config/lib/types
4
+ *
5
+ * SRP (Single Responsibility)
6
+ * - Define the shared TypeScript contracts for HAL/OpenClaw runtime context:
7
+ * global platform state plus calling-agent state used by skills at runtime.
8
+ *
9
+ * Interface Tree
10
+ HalRuntimeContext
11
+ ├── general: HalGeneralState
12
+ │ ├── openClawPaths
13
+ │ │ ├── openClawRoot
14
+ │ │ ├── openClawAgentRoot
15
+ │ │ ├── openClawConfigFile
16
+ │ │ └── openClawSharedAgentWorkspace
17
+ │ ├── halPaths
18
+ │ │ ├── halRoot
19
+ │ │ ├── halSystemConfigDir
20
+ │ │ └── halSystemConfigFile
21
+ │ ├── masterTemplates
22
+ │ │ ├── masterUserPath (e.g. {openClawSharedAgentWorkspace}/USER.md)
23
+ │ │ └── masterSafetyPath (e.g. {openClawSharedAgentWorkspace}/SAFETY.md)
24
+ │ ├── vaults: HalVaultEntry[]
25
+ │ │ └── HalVaultEntry
26
+ │ │ ├── name
27
+ │ │ ├── label
28
+ │ │ ├── role (master|standard)
29
+ │ │ ├── absolutePath
30
+ │ │ ├── directoryName
31
+ │ │ └── knownAreas
32
+ │ │ ├── projectsPath
33
+ │ │ ├── dailyNotesPath
34
+ │ │ ├── emailPath
35
+ │ │ ├── meetingsPath
36
+ │ │ └── peoplePath
37
+ │ ├── masterVault
38
+ │ │ ├── name
39
+ │ │ ├── label
40
+ │ │ ├── absolutePath
41
+ │ │ └── directoryName
42
+ │ ├── communicationAccounts: HalCommunicationAccountEntry[]
43
+ │ │ └── HalCommunicationAccountEntry
44
+ │ │ ├── label
45
+ │ │ ├── provider
46
+ │ │ ├── email?
47
+ │ │ └── scopes?
48
+ │ ├── settings
49
+ │ │ ├── timezone
50
+ │ │ └── workHours
51
+ │ │ ├── start
52
+ │ │ └── end
53
+ │ └── skills: Record<string, HalSkillRuntimeEntry>
54
+ │ └── HalSkillRuntimeEntry
55
+ │ ├── enabled
56
+ │ ├── homeAgent
57
+ │ ├── binaries[]
58
+ │ ├── configPath
59
+ │ └── configDir
60
+ └── callingAgent: HalCallingAgentState
61
+ ├── id
62
+ ├── workspacePath
63
+ ├── identityFiles
64
+ │ ├── soul
65
+ │ ├── agents
66
+ │ ├── identity
67
+ │ ├── heartbeat
68
+ │ ├── tools
69
+ │ ├── user
70
+ │ └── safety
71
+ ├── stores
72
+ │ ├── memoryDir
73
+ │ ├── knowledgeDir
74
+ │ └── skillsDir
75
+ ├── skillPaths
76
+ │ └── bySkill: Record<string, HalSkillPathSet>
77
+ │ └── HalSkillPathSet
78
+ │ ├── skillName
79
+ │ ├── sharedConfigDir
80
+ │ ├── sharedLogDir
81
+ │ ├── agentWorkingDir
82
+ │ ├── agentStateDir
83
+ │ ├── agentCacheDir
84
+ │ ├── agentTmpDir
85
+ │ └── agentSweepDir
86
+ └── capabilities
87
+ ├── canWriteAgentWorkspace
88
+ ├── canWriteSharedWorkspace
89
+ └── isHomeAgentForSkill: Record<string, boolean>
90
+ * =============================================================================
91
+ */
92
+ export type VaultRole = 'master' | 'standard';
93
+
94
+ export interface HalVaultAreas {
95
+ projectsFolder: string;
96
+ dailyNotesFolder: string;
97
+ emailFolder: string;
98
+ meetingsFolder: string;
99
+ peopleFolder: string;
100
+ }
101
+
102
+ export interface HalVaultResolvedAreas {
103
+ projectsPath: string;
104
+ dailyNotesPath: string;
105
+ emailPath: string;
106
+ meetingsPath: string;
107
+ peoplePath: string;
108
+ }
109
+
110
+ export interface HalVaultEntry {
111
+ name: string;
112
+ label: string;
113
+ role: VaultRole;
114
+ absolutePath: string;
115
+ directoryName: string;
116
+ knownAreas: HalVaultResolvedAreas;
117
+ }
118
+
119
+ export interface HalMasterVaultRef {
120
+ name: string;
121
+ label: string;
122
+ absolutePath: string;
123
+ directoryName: string;
124
+ }
125
+
126
+ export interface HalCommunicationAccountEntry {
127
+ label: string;
128
+ provider: string;
129
+ email?: string;
130
+ scopes?: string[];
131
+ [key: string]: unknown;
132
+ }
133
+
134
+ export interface HalSkillRuntimeEntry {
135
+ enabled: boolean;
136
+ homeAgent: string | null;
137
+ binaries: string[];
138
+ configPath: string;
139
+ configDir: string;
140
+ }
141
+
142
+ export interface HalSkillPathSet {
143
+ skillName: string;
144
+ sharedConfigDir: string;
145
+ sharedLogDir: string;
146
+ agentWorkingDir: string;
147
+ agentStateDir: string;
148
+ agentCacheDir: string;
149
+ agentTmpDir: string;
150
+ agentSweepDir: string;
151
+ }
152
+
153
+ export interface HalGeneralState {
154
+ openClawPaths: {
155
+ openClawRoot: string;
156
+ openClawAgentRoot: string;
157
+ openClawConfigFile: string;
158
+ openClawSharedAgentWorkspace: string;
159
+ };
160
+
161
+ halPaths: {
162
+ halRoot: string;
163
+ halSystemConfigDir: string;
164
+ halSystemConfigFile: string;
165
+ };
166
+
167
+ masterTemplates: {
168
+ masterUserPath: string;
169
+ masterSafetyPath: string;
170
+ };
171
+
172
+ vaults: HalVaultEntry[];
173
+ masterVault: HalMasterVaultRef | null;
174
+ communicationAccounts: HalCommunicationAccountEntry[];
175
+
176
+ settings: {
177
+ timezone: string | null;
178
+ workHours: { start: string; end: string } | null;
179
+ };
180
+
181
+ skills: Record<string, HalSkillRuntimeEntry>;
182
+ }
183
+
184
+ export interface HalCallingAgentState {
185
+ id: string | null;
186
+ workspacePath: string | null;
187
+
188
+ identityFiles: {
189
+ soul: string | null;
190
+ agents: string | null;
191
+ identity: string | null;
192
+ heartbeat: string | null;
193
+ tools: string | null;
194
+ user: string | null;
195
+ safety: string | null;
196
+ };
197
+
198
+ stores: {
199
+ memoryDir: string | null;
200
+ knowledgeDir: string | null;
201
+ skillsDir: string | null;
202
+ };
203
+
204
+ skillPaths: {
205
+ bySkill: Record<string, HalSkillPathSet>;
206
+ };
207
+
208
+ capabilities: {
209
+ canWriteAgentWorkspace: boolean;
210
+ canWriteSharedWorkspace: boolean;
211
+ isHomeAgentForSkill: Record<string, boolean>;
212
+ };
213
+ }
214
+
215
+ export interface HalRuntimeContext {
216
+ general: HalGeneralState;
217
+ callingAgent: HalCallingAgentState;
218
+ }
package/package.json CHANGED
@@ -1,16 +1,35 @@
1
1
  {
2
2
  "name": "@jamaynor/hal-config",
3
- "version": "1.0.2",
3
+ "version": "1.1.1",
4
4
  "description": "Shared configuration loader for HAL OpenClaw skills — reads system-config.json, provides skill discovery, and runtime registration",
5
5
  "type": "module",
6
6
  "main": "index.js",
7
+ "types": "index.d.ts",
8
+ "files": [
9
+ "index.js",
10
+ "index.d.ts",
11
+ "test-utils.js",
12
+ "test-utils.d.ts",
13
+ "lib/",
14
+ "security/",
15
+ "package.json"
16
+ ],
7
17
  "publishConfig": {
8
18
  "access": "public"
9
19
  },
10
20
  "exports": {
11
- ".": "./index.js",
12
- "./test-utils": "./test-utils.js",
13
- "./security": "./security/index.js"
21
+ ".": {
22
+ "types": "./index.d.ts",
23
+ "default": "./index.js"
24
+ },
25
+ "./test-utils": {
26
+ "types": "./test-utils.d.ts",
27
+ "default": "./test-utils.js"
28
+ },
29
+ "./security": {
30
+ "types": "./security/index.d.ts",
31
+ "default": "./security/index.js"
32
+ }
14
33
  },
15
34
  "scripts": {
16
35
  "test": "node --test test/test.js test/test-utils.test.js test/config-io.test.js test/security.test.js"