@united-workforce/cli 0.1.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/LICENSE +21 -0
- package/README.md +221 -0
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js +147 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +685 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/current-role.test.d.ts +2 -0
- package/dist/__tests__/current-role.test.d.ts.map +1 -0
- package/dist/__tests__/current-role.test.js +401 -0
- package/dist/__tests__/current-role.test.js.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.d.ts +2 -0
- package/dist/__tests__/e2e-mock-agent.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.js +401 -0
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -0
- package/dist/__tests__/include-tag.test.d.ts +2 -0
- package/dist/__tests__/include-tag.test.d.ts.map +1 -0
- package/dist/__tests__/include-tag.test.js +69 -0
- package/dist/__tests__/include-tag.test.js.map +1 -0
- package/dist/__tests__/log.test.d.ts +2 -0
- package/dist/__tests__/log.test.d.ts.map +1 -0
- package/dist/__tests__/log.test.js +161 -0
- package/dist/__tests__/log.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.d.ts +2 -0
- package/dist/__tests__/moderator-evaluate.test.d.ts.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +170 -0
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -0
- package/dist/__tests__/preload.d.ts +3 -0
- package/dist/__tests__/preload.d.ts.map +1 -0
- package/dist/__tests__/preload.js +6 -0
- package/dist/__tests__/preload.js.map +1 -0
- package/dist/__tests__/prompt.test.d.ts +2 -0
- package/dist/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/__tests__/prompt.test.js +111 -0
- package/dist/__tests__/prompt.test.js.map +1 -0
- package/dist/__tests__/resolve-head-hash.test.d.ts +2 -0
- package/dist/__tests__/resolve-head-hash.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-head-hash.test.js +66 -0
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -0
- package/dist/__tests__/setup-agent-discovery.test.d.ts +2 -0
- package/dist/__tests__/setup-agent-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/setup-agent-discovery.test.js +119 -0
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -0
- package/dist/__tests__/setup-complexity.test.d.ts +2 -0
- package/dist/__tests__/setup-complexity.test.d.ts.map +1 -0
- package/dist/__tests__/setup-complexity.test.js +314 -0
- package/dist/__tests__/setup-complexity.test.js.map +1 -0
- package/dist/__tests__/setup-validate.test.d.ts +2 -0
- package/dist/__tests__/setup-validate.test.d.ts.map +1 -0
- package/dist/__tests__/setup-validate.test.js +108 -0
- package/dist/__tests__/setup-validate.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.d.ts +2 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.d.ts.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +107 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -0
- package/dist/__tests__/spawn-agent-json.test.d.ts +2 -0
- package/dist/__tests__/spawn-agent-json.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-agent-json.test.js +79 -0
- package/dist/__tests__/spawn-agent-json.test.js.map +1 -0
- package/dist/__tests__/step-read.test.d.ts +2 -0
- package/dist/__tests__/step-read.test.d.ts.map +1 -0
- package/dist/__tests__/step-read.test.js +561 -0
- package/dist/__tests__/step-read.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.d.ts +2 -0
- package/dist/__tests__/step-show-json.test.d.ts.map +1 -0
- package/dist/__tests__/step-show-json.test.js +311 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -0
- package/dist/__tests__/step-timing.test.d.ts +2 -0
- package/dist/__tests__/step-timing.test.d.ts.map +1 -0
- package/dist/__tests__/step-timing.test.js +345 -0
- package/dist/__tests__/step-timing.test.js.map +1 -0
- package/dist/__tests__/store-global-cas.test.d.ts +2 -0
- package/dist/__tests__/store-global-cas.test.d.ts.map +1 -0
- package/dist/__tests__/store-global-cas.test.js +235 -0
- package/dist/__tests__/store-global-cas.test.js.map +1 -0
- package/dist/__tests__/store-storage-root.test.d.ts +2 -0
- package/dist/__tests__/store-storage-root.test.d.ts.map +1 -0
- package/dist/__tests__/store-storage-root.test.js +43 -0
- package/dist/__tests__/store-storage-root.test.js.map +1 -0
- package/dist/__tests__/store-unified-threads.test.d.ts +2 -0
- package/dist/__tests__/store-unified-threads.test.d.ts.map +1 -0
- package/dist/__tests__/store-unified-threads.test.js +189 -0
- package/dist/__tests__/store-unified-threads.test.js.map +1 -0
- package/dist/__tests__/thread-cancel-status.test.d.ts +2 -0
- package/dist/__tests__/thread-cancel-status.test.d.ts.map +1 -0
- package/dist/__tests__/thread-cancel-status.test.js +111 -0
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -0
- package/dist/__tests__/thread-list-filters.test.d.ts +2 -0
- package/dist/__tests__/thread-list-filters.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-filters.test.js +442 -0
- package/dist/__tests__/thread-list-filters.test.js.map +1 -0
- package/dist/__tests__/thread-location.test.d.ts +2 -0
- package/dist/__tests__/thread-location.test.d.ts.map +1 -0
- package/dist/__tests__/thread-location.test.js +159 -0
- package/dist/__tests__/thread-location.test.js.map +1 -0
- package/dist/__tests__/thread-read-quota.test.d.ts +2 -0
- package/dist/__tests__/thread-read-quota.test.d.ts.map +1 -0
- package/dist/__tests__/thread-read-quota.test.js +546 -0
- package/dist/__tests__/thread-read-quota.test.js.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.d.ts +2 -0
- package/dist/__tests__/thread-read-xml-tags.test.d.ts.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.js +610 -0
- package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -0
- package/dist/__tests__/thread-resume.test.d.ts +2 -0
- package/dist/__tests__/thread-resume.test.d.ts.map +1 -0
- package/dist/__tests__/thread-resume.test.js +592 -0
- package/dist/__tests__/thread-resume.test.js.map +1 -0
- package/dist/__tests__/thread-show-status.test.d.ts +2 -0
- package/dist/__tests__/thread-show-status.test.d.ts.map +1 -0
- package/dist/__tests__/thread-show-status.test.js +267 -0
- package/dist/__tests__/thread-show-status.test.js.map +1 -0
- package/dist/__tests__/thread-start-cwd-cli.test.d.ts +2 -0
- package/dist/__tests__/thread-start-cwd-cli.test.d.ts.map +1 -0
- package/dist/__tests__/thread-start-cwd-cli.test.js +130 -0
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -0
- package/dist/__tests__/thread-step-count.test.d.ts +2 -0
- package/dist/__tests__/thread-step-count.test.d.ts.map +1 -0
- package/dist/__tests__/thread-step-count.test.js +55 -0
- package/dist/__tests__/thread-step-count.test.js.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.d.ts +2 -0
- package/dist/__tests__/thread-suspend-step.test.d.ts.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.js +155 -0
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -0
- package/dist/__tests__/thread-suspended-display.test.d.ts +2 -0
- package/dist/__tests__/thread-suspended-display.test.d.ts.map +1 -0
- package/dist/__tests__/thread-suspended-display.test.js +247 -0
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -0
- package/dist/__tests__/thread-test-helpers.d.ts +4 -0
- package/dist/__tests__/thread-test-helpers.d.ts.map +1 -0
- package/dist/__tests__/thread-test-helpers.js +23 -0
- package/dist/__tests__/thread-test-helpers.js.map +1 -0
- package/dist/__tests__/thread.test.d.ts +2 -0
- package/dist/__tests__/thread.test.d.ts.map +1 -0
- package/dist/__tests__/thread.test.js +883 -0
- package/dist/__tests__/thread.test.js.map +1 -0
- package/dist/__tests__/validate-semantic.test.d.ts +2 -0
- package/dist/__tests__/validate-semantic.test.d.ts.map +1 -0
- package/dist/__tests__/validate-semantic.test.js +408 -0
- package/dist/__tests__/validate-semantic.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +308 -0
- package/dist/__tests__/workflow-resolution.test.js.map +1 -0
- package/dist/background/background.d.ts +38 -0
- package/dist/background/background.d.ts.map +1 -0
- package/dist/background/background.js +123 -0
- package/dist/background/background.js.map +1 -0
- package/dist/background/index.d.ts +3 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +2 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/types.d.ts +9 -0
- package/dist/background/types.d.ts.map +1 -0
- package/dist/background/types.js +2 -0
- package/dist/background/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +535 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +41 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +252 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/log.d.ts +26 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +79 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/prompt.d.ts +6 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/prompt.js +67 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/setup.d.ts +73 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +522 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/shared.d.ts +31 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +154 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/step.d.ts +18 -0
- package/dist/commands/step.d.ts.map +1 -0
- package/dist/commands/step.js +257 -0
- package/dist/commands/step.js.map +1 -0
- package/dist/commands/thread-time-parser.d.ts +6 -0
- package/dist/commands/thread-time-parser.d.ts.map +1 -0
- package/dist/commands/thread-time-parser.js +22 -0
- package/dist/commands/thread-time-parser.js.map +1 -0
- package/dist/commands/thread.d.ts +38 -0
- package/dist/commands/thread.d.ts.map +1 -0
- package/dist/commands/thread.js +1087 -0
- package/dist/commands/thread.js.map +1 -0
- package/dist/commands/workflow.d.ts +24 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +138 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +10 -0
- package/dist/format.js.map +1 -0
- package/dist/include.d.ts +12 -0
- package/dist/include.d.ts.map +1 -0
- package/dist/include.js +35 -0
- package/dist/include.js.map +1 -0
- package/dist/moderator/__tests__/evaluate.test.d.ts +2 -0
- package/dist/moderator/__tests__/evaluate.test.d.ts.map +1 -0
- package/dist/moderator/__tests__/evaluate.test.js +167 -0
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -0
- package/dist/moderator/evaluate.d.ts +6 -0
- package/dist/moderator/evaluate.d.ts.map +1 -0
- package/dist/moderator/evaluate.js +65 -0
- package/dist/moderator/evaluate.js.map +1 -0
- package/dist/moderator/index.d.ts +4 -0
- package/dist/moderator/index.d.ts.map +1 -0
- package/dist/moderator/index.js +3 -0
- package/dist/moderator/index.js.map +1 -0
- package/dist/moderator/types.d.ts +25 -0
- package/dist/moderator/types.d.ts.map +1 -0
- package/dist/moderator/types.js +4 -0
- package/dist/moderator/types.js.map +1 -0
- package/dist/schemas.d.ts +16 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +17 -0
- package/dist/schemas.js.map +1 -0
- package/dist/store.d.ts +77 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +392 -0
- package/dist/store.js.map +1 -0
- package/dist/validate-semantic.d.ts +7 -0
- package/dist/validate-semantic.d.ts.map +1 -0
- package/dist/validate-semantic.js +263 -0
- package/dist/validate-semantic.js.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +115 -0
- package/dist/validate.js.map +1 -0
- package/package.json +44 -0
- package/src/__tests__/adapter-json-roundtrip.test.ts +181 -0
- package/src/__tests__/config.test.ts +740 -0
- package/src/__tests__/current-role.test.ts +438 -0
- package/src/__tests__/e2e-mock-agent.test.ts +498 -0
- package/src/__tests__/fixtures/e2e-completed-resume.mock.yaml +15 -0
- package/src/__tests__/fixtures/e2e-count.mock.yaml +19 -0
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +45 -0
- package/src/__tests__/fixtures/e2e-linear.mock.yaml +13 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +32 -0
- package/src/__tests__/fixtures/e2e-loop.mock.yaml +25 -0
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +36 -0
- package/src/__tests__/fixtures/e2e-mismatch.mock.yaml +16 -0
- package/src/__tests__/fixtures/e2e-mustache.mock.yaml +15 -0
- package/src/__tests__/fixtures/e2e-mustache.workflow.yaml +34 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +14 -0
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +24 -0
- package/src/__tests__/include-tag.test.ts +84 -0
- package/src/__tests__/log.test.ts +181 -0
- package/src/__tests__/moderator-evaluate.test.ts +186 -0
- package/src/__tests__/preload.ts +7 -0
- package/src/__tests__/prompt.test.ts +129 -0
- package/src/__tests__/resolve-head-hash.test.ts +86 -0
- package/src/__tests__/setup-agent-discovery.test.ts +167 -0
- package/src/__tests__/setup-complexity.test.ts +381 -0
- package/src/__tests__/setup-validate.test.ts +148 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +144 -0
- package/src/__tests__/spawn-agent-json.test.ts +100 -0
- package/src/__tests__/step-read.test.ts +632 -0
- package/src/__tests__/step-show-json.test.ts +373 -0
- package/src/__tests__/step-timing.test.ts +392 -0
- package/src/__tests__/store-global-cas.test.ts +308 -0
- package/src/__tests__/store-storage-root.test.ts +49 -0
- package/src/__tests__/store-unified-threads.test.ts +235 -0
- package/src/__tests__/thread-cancel-status.test.ts +138 -0
- package/src/__tests__/thread-list-filters.test.ts +572 -0
- package/src/__tests__/thread-location.test.ts +186 -0
- package/src/__tests__/thread-read-quota.test.ts +613 -0
- package/src/__tests__/thread-read-xml-tags.test.ts +717 -0
- package/src/__tests__/thread-resume.test.ts +710 -0
- package/src/__tests__/thread-show-status.test.ts +317 -0
- package/src/__tests__/thread-start-cwd-cli.test.ts +164 -0
- package/src/__tests__/thread-step-count.test.ts +70 -0
- package/src/__tests__/thread-suspend-step.test.ts +181 -0
- package/src/__tests__/thread-suspended-display.test.ts +287 -0
- package/src/__tests__/thread-test-helpers.ts +37 -0
- package/src/__tests__/thread.test.ts +1025 -0
- package/src/__tests__/validate-semantic.test.ts +474 -0
- package/src/__tests__/workflow-resolution.test.ts +421 -0
- package/src/background/background.ts +147 -0
- package/src/background/index.ts +11 -0
- package/src/background/types.ts +9 -0
- package/src/cli.ts +692 -0
- package/src/commands/config.ts +304 -0
- package/src/commands/log.ts +116 -0
- package/src/commands/prompt.ts +81 -0
- package/src/commands/setup.ts +603 -0
- package/src/commands/shared.ts +227 -0
- package/src/commands/step.ts +343 -0
- package/src/commands/thread-time-parser.ts +23 -0
- package/src/commands/thread.ts +1575 -0
- package/src/commands/workflow.ts +213 -0
- package/src/format.ts +12 -0
- package/src/include.ts +37 -0
- package/src/moderator/__tests__/evaluate.test.ts +199 -0
- package/src/moderator/evaluate.ts +80 -0
- package/src/moderator/index.ts +7 -0
- package/src/moderator/types.ts +24 -0
- package/src/schemas.ts +26 -0
- package/src/store.ts +479 -0
- package/src/validate-semantic.ts +304 -0
- package/src/validate.ts +137 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse, stringify } from "yaml";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Valid configuration key schema
|
|
7
|
+
*/
|
|
8
|
+
const VALID_CONFIG_KEYS: Record<
|
|
9
|
+
string,
|
|
10
|
+
{ nested: boolean; knownFields?: string[]; minDepth?: number }
|
|
11
|
+
> = {
|
|
12
|
+
providers: {
|
|
13
|
+
nested: true,
|
|
14
|
+
knownFields: ["baseUrl", "apiKey"],
|
|
15
|
+
},
|
|
16
|
+
models: {
|
|
17
|
+
nested: true,
|
|
18
|
+
knownFields: ["provider", "name"],
|
|
19
|
+
},
|
|
20
|
+
agents: {
|
|
21
|
+
nested: true,
|
|
22
|
+
knownFields: ["command", "args"],
|
|
23
|
+
},
|
|
24
|
+
agentOverrides: {
|
|
25
|
+
nested: true,
|
|
26
|
+
// agentOverrides.<workflowName>.<roleName> = agentAlias (string value)
|
|
27
|
+
// No knownFields — workflow/role names are user-defined
|
|
28
|
+
},
|
|
29
|
+
modelOverrides: {
|
|
30
|
+
nested: true,
|
|
31
|
+
minDepth: 2,
|
|
32
|
+
// modelOverrides.<scenario> = modelAlias (string value)
|
|
33
|
+
// No knownFields — scenarios are user-defined
|
|
34
|
+
},
|
|
35
|
+
defaultAgent: { nested: false },
|
|
36
|
+
defaultModel: { nested: false },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Validate a config key path against the known schema
|
|
41
|
+
*/
|
|
42
|
+
function validateConfigKey(path: string[]): void {
|
|
43
|
+
if (path.length === 0) {
|
|
44
|
+
throw new Error("Path cannot be empty");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const topLevel = path[0];
|
|
48
|
+
const schema = VALID_CONFIG_KEYS[topLevel];
|
|
49
|
+
|
|
50
|
+
if (!schema) {
|
|
51
|
+
const validKeys = Object.keys(VALID_CONFIG_KEYS).join(", ");
|
|
52
|
+
throw new Error(`Unknown config key: ${topLevel}. Valid top-level keys are: ${validKeys}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Scalar keys cannot have nested paths
|
|
56
|
+
if (!schema.nested && path.length > 1) {
|
|
57
|
+
throw new Error(`${topLevel} is a scalar key and cannot have nested properties`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Nested keys must have at least minDepth segments (default 3)
|
|
61
|
+
const minDepth = schema.minDepth ?? 3;
|
|
62
|
+
if (schema.nested && path.length < minDepth) {
|
|
63
|
+
const fields = schema.knownFields?.join(", ") ?? "";
|
|
64
|
+
throw new Error(
|
|
65
|
+
`Incomplete path for ${topLevel}. Must specify a field (e.g., ${topLevel}.<name>.<field>). Valid fields: ${fields}`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Validate the field name for nested keys
|
|
70
|
+
if (schema.nested && path.length >= 3 && schema.knownFields) {
|
|
71
|
+
const field = path[path.length - 1];
|
|
72
|
+
if (!schema.knownFields.includes(field)) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Unknown field '${field}' in ${topLevel}. Valid fields are: ${schema.knownFields.join(", ")}`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns the path to the config.yaml file
|
|
82
|
+
*/
|
|
83
|
+
export function getConfigPath(storageRoot: string): string {
|
|
84
|
+
return join(storageRoot, "config.yaml");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Load and parse YAML config file
|
|
89
|
+
*/
|
|
90
|
+
export function loadConfig(configPath: string): Record<string, unknown> {
|
|
91
|
+
if (!existsSync(configPath)) {
|
|
92
|
+
throw new Error(`Config file not found: ${configPath}`);
|
|
93
|
+
}
|
|
94
|
+
const content = readFileSync(configPath, "utf8");
|
|
95
|
+
if (!content.trim()) {
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const parsed = parse(content);
|
|
100
|
+
return (parsed ?? {}) as Record<string, unknown>;
|
|
101
|
+
} catch (error) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Invalid YAML in config file: ${error instanceof Error ? error.message : String(error)}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Save config as YAML
|
|
110
|
+
*/
|
|
111
|
+
export function saveConfig(configPath: string, config: Record<string, unknown>): void {
|
|
112
|
+
const dir = join(configPath, "..");
|
|
113
|
+
if (!existsSync(dir)) {
|
|
114
|
+
mkdirSync(dir, { recursive: true });
|
|
115
|
+
}
|
|
116
|
+
const yaml = stringify(config);
|
|
117
|
+
writeFileSync(configPath, yaml, "utf8");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse dot-notation key into path segments
|
|
122
|
+
*/
|
|
123
|
+
export function parseDotPath(key: string): string[] {
|
|
124
|
+
return key.split(".");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get nested value from object using path array
|
|
129
|
+
*/
|
|
130
|
+
export function getNestedValue(obj: Record<string, unknown>, path: string[]): unknown {
|
|
131
|
+
let current: unknown = obj;
|
|
132
|
+
for (const segment of path) {
|
|
133
|
+
if (current === null || current === undefined || typeof current !== "object") {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
current = (current as Record<string, unknown>)[segment];
|
|
137
|
+
}
|
|
138
|
+
return current;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Set nested value in object using path array (mutates obj)
|
|
143
|
+
*/
|
|
144
|
+
export function setNestedValue(obj: Record<string, unknown>, path: string[], value: unknown): void {
|
|
145
|
+
if (path.length === 0) {
|
|
146
|
+
throw new Error("Path cannot be empty");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let current: Record<string, unknown> = obj;
|
|
150
|
+
|
|
151
|
+
// Navigate/create to the parent of the target
|
|
152
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
153
|
+
const segment = path[i];
|
|
154
|
+
const next = current[segment];
|
|
155
|
+
|
|
156
|
+
if (next === null || next === undefined) {
|
|
157
|
+
// Create intermediate object
|
|
158
|
+
const newObj: Record<string, unknown> = {};
|
|
159
|
+
current[segment] = newObj;
|
|
160
|
+
current = newObj;
|
|
161
|
+
} else if (typeof next === "object" && !Array.isArray(next)) {
|
|
162
|
+
// Navigate into existing object
|
|
163
|
+
current = next as Record<string, unknown>;
|
|
164
|
+
} else {
|
|
165
|
+
// Cannot navigate into non-object
|
|
166
|
+
throw new Error(
|
|
167
|
+
`Cannot set property '${path[i + 1]}' on non-object at path '${path.slice(0, i + 1).join(".")}'`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Set the final value
|
|
173
|
+
const lastSegment = path[path.length - 1];
|
|
174
|
+
current[lastSegment] = value;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Deep clone and mask all apiKey values in providers section
|
|
179
|
+
*/
|
|
180
|
+
export function maskApiKeys(config: Record<string, unknown>): Record<string, unknown> {
|
|
181
|
+
// Deep clone
|
|
182
|
+
const cloned = JSON.parse(JSON.stringify(config)) as Record<string, unknown>;
|
|
183
|
+
|
|
184
|
+
// Mask apiKey values in providers
|
|
185
|
+
if (cloned.providers && typeof cloned.providers === "object") {
|
|
186
|
+
const providers = cloned.providers as Record<string, unknown>;
|
|
187
|
+
for (const providerName of Object.keys(providers)) {
|
|
188
|
+
const provider = providers[providerName];
|
|
189
|
+
if (provider && typeof provider === "object") {
|
|
190
|
+
const providerObj = provider as Record<string, unknown>;
|
|
191
|
+
if ("apiKey" in providerObj) {
|
|
192
|
+
providerObj.apiKey = "***MASKED***";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return cloned;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* List all configuration values (masks API keys)
|
|
203
|
+
*/
|
|
204
|
+
export async function cmdConfigList(storageRoot: string): Promise<unknown> {
|
|
205
|
+
const configPath = getConfigPath(storageRoot);
|
|
206
|
+
const config = loadConfig(configPath);
|
|
207
|
+
const masked = maskApiKeys(config);
|
|
208
|
+
return masked;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get a specific configuration value
|
|
213
|
+
*/
|
|
214
|
+
export async function cmdConfigGet(storageRoot: string, key: string): Promise<unknown> {
|
|
215
|
+
const configPath = getConfigPath(storageRoot);
|
|
216
|
+
const config = loadConfig(configPath);
|
|
217
|
+
const path = parseDotPath(key);
|
|
218
|
+
const value = getNestedValue(config, path);
|
|
219
|
+
|
|
220
|
+
if (value === undefined) {
|
|
221
|
+
throw new Error(`Key not found: ${key}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Parse value for args key (must be JSON array)
|
|
229
|
+
*/
|
|
230
|
+
function parseArgsValue(value: string): unknown {
|
|
231
|
+
if (value.startsWith("[")) {
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(value);
|
|
234
|
+
if (!Array.isArray(parsed)) {
|
|
235
|
+
throw new Error("Value must be an array");
|
|
236
|
+
}
|
|
237
|
+
return parsed;
|
|
238
|
+
} catch (error) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Invalid JSON array for args key: ${error instanceof Error ? error.message : String(error)}`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
throw new Error("Value for 'args' key must be a JSON array starting with '['");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Validate that we're not setting a property on a non-object
|
|
249
|
+
*/
|
|
250
|
+
function validateParentPath(
|
|
251
|
+
config: Record<string, unknown>,
|
|
252
|
+
path: string[],
|
|
253
|
+
lastSegment: string,
|
|
254
|
+
): void {
|
|
255
|
+
if (path.length > 1) {
|
|
256
|
+
const parentPath = path.slice(0, -1);
|
|
257
|
+
const parent = getNestedValue(config, parentPath);
|
|
258
|
+
if (parent !== null && parent !== undefined && typeof parent !== "object") {
|
|
259
|
+
throw new Error(
|
|
260
|
+
`Cannot set property '${lastSegment}' on non-object at path '${parentPath.join(".")}'`,
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Set a specific configuration value
|
|
268
|
+
*/
|
|
269
|
+
export async function cmdConfigSet(
|
|
270
|
+
storageRoot: string,
|
|
271
|
+
key: string,
|
|
272
|
+
value: string,
|
|
273
|
+
): Promise<unknown> {
|
|
274
|
+
const configPath = getConfigPath(storageRoot);
|
|
275
|
+
|
|
276
|
+
// Load existing config or create empty one
|
|
277
|
+
let config: Record<string, unknown>;
|
|
278
|
+
if (existsSync(configPath)) {
|
|
279
|
+
config = loadConfig(configPath);
|
|
280
|
+
} else {
|
|
281
|
+
config = {};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const path = parseDotPath(key);
|
|
285
|
+
|
|
286
|
+
// Validate the key path
|
|
287
|
+
validateConfigKey(path);
|
|
288
|
+
|
|
289
|
+
const lastSegment = path[path.length - 1];
|
|
290
|
+
|
|
291
|
+
// Parse value if it's for an array key (args)
|
|
292
|
+
let parsedValue: unknown = value;
|
|
293
|
+
if (lastSegment === "args") {
|
|
294
|
+
parsedValue = parseArgsValue(value);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Validate we're not setting a property on a non-object
|
|
298
|
+
validateParentPath(config, path, lastSegment);
|
|
299
|
+
|
|
300
|
+
setNestedValue(config, path, parsedValue);
|
|
301
|
+
saveConfig(configPath, config);
|
|
302
|
+
|
|
303
|
+
return { key, value: parsedValue };
|
|
304
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readdir, readFile, stat, unlink } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
type LogListItem = {
|
|
5
|
+
name: string;
|
|
6
|
+
size: number;
|
|
7
|
+
date: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type LogShowFilter = {
|
|
11
|
+
thread: string | null;
|
|
12
|
+
process: string | null;
|
|
13
|
+
date: string | null;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type LogEntry = {
|
|
17
|
+
ts: string;
|
|
18
|
+
pid: string;
|
|
19
|
+
tag: string;
|
|
20
|
+
msg: string;
|
|
21
|
+
thread: string | null;
|
|
22
|
+
workflow: string | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type LogCleanResult = {
|
|
26
|
+
deleted: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function logsDir(storageRoot: string): string {
|
|
30
|
+
return join(storageRoot, "logs");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function listLogFiles(dir: string): Promise<Array<string>> {
|
|
34
|
+
try {
|
|
35
|
+
const files = await readdir(dir);
|
|
36
|
+
return files.filter((f) => f.endsWith(".jsonl")).sort();
|
|
37
|
+
} catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function dateFromFilename(name: string): string {
|
|
43
|
+
return name.replace(".jsonl", "");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function parseJsonlFile(path: string): Promise<Array<LogEntry>> {
|
|
47
|
+
const content = await readFile(path, "utf-8");
|
|
48
|
+
const lines = content
|
|
49
|
+
.trim()
|
|
50
|
+
.split("\n")
|
|
51
|
+
.filter((l) => l.length > 0);
|
|
52
|
+
return lines.map((line) => JSON.parse(line) as LogEntry);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function cmdLogList(storageRoot: string): Promise<Array<LogListItem>> {
|
|
56
|
+
const dir = logsDir(storageRoot);
|
|
57
|
+
const files = await listLogFiles(dir);
|
|
58
|
+
const items: Array<LogListItem> = [];
|
|
59
|
+
for (const name of files) {
|
|
60
|
+
const s = await stat(join(dir, name));
|
|
61
|
+
items.push({ name, size: s.size, date: dateFromFilename(name) });
|
|
62
|
+
}
|
|
63
|
+
// sort by date descending
|
|
64
|
+
items.sort((a, b) => (a.date > b.date ? -1 : a.date < b.date ? 1 : 0));
|
|
65
|
+
return items;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function cmdLogShow(
|
|
69
|
+
storageRoot: string,
|
|
70
|
+
filter: LogShowFilter,
|
|
71
|
+
): Promise<Array<LogEntry>> {
|
|
72
|
+
const dir = logsDir(storageRoot);
|
|
73
|
+
let files: Array<string>;
|
|
74
|
+
|
|
75
|
+
if (filter.date !== null) {
|
|
76
|
+
files = [`${filter.date}.jsonl`];
|
|
77
|
+
} else {
|
|
78
|
+
files = await listLogFiles(dir);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let entries: Array<LogEntry> = [];
|
|
82
|
+
for (const file of files) {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = await parseJsonlFile(join(dir, file));
|
|
85
|
+
entries = entries.concat(parsed);
|
|
86
|
+
} catch {
|
|
87
|
+
// file doesn't exist or is unreadable, skip
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (filter.thread !== null) {
|
|
92
|
+
entries = entries.filter((e) => e.thread === filter.thread);
|
|
93
|
+
}
|
|
94
|
+
if (filter.process !== null) {
|
|
95
|
+
entries = entries.filter((e) => e.pid === filter.process);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
entries.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));
|
|
99
|
+
return entries;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function cmdLogClean(storageRoot: string, before: string): Promise<LogCleanResult> {
|
|
103
|
+
const dir = logsDir(storageRoot);
|
|
104
|
+
const files = await listLogFiles(dir);
|
|
105
|
+
let deleted = 0;
|
|
106
|
+
|
|
107
|
+
for (const name of files) {
|
|
108
|
+
const date = dateFromFilename(name);
|
|
109
|
+
if (date < before) {
|
|
110
|
+
await unlink(join(dir, name));
|
|
111
|
+
deleted++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { deleted };
|
|
116
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateAdapterDevelopingReference,
|
|
3
|
+
generateBootstrapReference,
|
|
4
|
+
generateUsageReference,
|
|
5
|
+
generateWorkflowAuthoringReference,
|
|
6
|
+
} from "@united-workforce/util";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
generateAdapterDevelopingReference as cmdPromptAdapterDeveloping,
|
|
10
|
+
generateBootstrapReference as cmdPromptBootstrap,
|
|
11
|
+
generateUsageReference as cmdPromptUsageReference,
|
|
12
|
+
generateWorkflowAuthoringReference as cmdPromptWorkflowAuthoring,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const PROMPT_ENTRIES: ReadonlyArray<{ name: string; generate: () => string }> = [
|
|
16
|
+
{ name: "usage", generate: generateUsageReference },
|
|
17
|
+
{ name: "workflow-authoring", generate: generateWorkflowAuthoringReference },
|
|
18
|
+
{ name: "adapter-developing", generate: generateAdapterDevelopingReference },
|
|
19
|
+
{ name: "bootstrap", generate: generateBootstrapReference },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export function cmdPromptList(): ReadonlyArray<string> {
|
|
23
|
+
return PROMPT_ENTRIES.map((e) => e.name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function cmdPromptUsage(): string {
|
|
27
|
+
return PROMPT_ENTRIES.filter((e) => e.name !== "bootstrap")
|
|
28
|
+
.map((e) => e.generate())
|
|
29
|
+
.join("\n\n---\n\n");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function cmdPromptSetup(): string {
|
|
33
|
+
return `# uwf Skill Setup
|
|
34
|
+
|
|
35
|
+
You are being asked to install or update the uwf (United Workforce) skill
|
|
36
|
+
so that you know how to use the \`uwf\` CLI for workflow orchestration.
|
|
37
|
+
|
|
38
|
+
## Steps
|
|
39
|
+
|
|
40
|
+
1. **Check if uwf CLI is installed:**
|
|
41
|
+
\`\`\`bash
|
|
42
|
+
uwf --help
|
|
43
|
+
\`\`\`
|
|
44
|
+
If not installed: \`npm install -g @united-workforce/cli\`
|
|
45
|
+
|
|
46
|
+
2. **Clean up old versions of the skill** (if any exist):
|
|
47
|
+
- Look for any existing uwf/workflow skill files in your skill directories
|
|
48
|
+
(e.g. \`~/.hermes/skills/uwf/SKILL.md\` or similar paths).
|
|
49
|
+
- If a skill exists, read its \`version\` from the YAML frontmatter.
|
|
50
|
+
- If the version matches the current CLI version (\`uwf --version\`), you're done — no update needed.
|
|
51
|
+
- If the version is older, **delete the old skill file** before proceeding.
|
|
52
|
+
|
|
53
|
+
3. **Install the bootstrap skill:**
|
|
54
|
+
\`\`\`bash
|
|
55
|
+
uwf prompt bootstrap > ~/.hermes/skills/uwf/SKILL.md
|
|
56
|
+
\`\`\`
|
|
57
|
+
The bootstrap prompt already includes complete YAML frontmatter — no editing needed.
|
|
58
|
+
|
|
59
|
+
4. **Verify** the skill is loadable by your agent framework.
|
|
60
|
+
|
|
61
|
+
## Individual prompts
|
|
62
|
+
|
|
63
|
+
Each prompt outputs a complete SKILL.md with frontmatter — pipe directly to a file:
|
|
64
|
+
|
|
65
|
+
\`\`\`bash
|
|
66
|
+
uwf prompt list # list available prompt names
|
|
67
|
+
uwf prompt usage > ~/.hermes/skills/uwf-usage/SKILL.md # CLI usage guide
|
|
68
|
+
uwf prompt workflow-authoring > ~/.hermes/skills/uwf-workflow-authoring/SKILL.md
|
|
69
|
+
uwf prompt adapter-developing > ~/.hermes/skills/uwf-adapter-developing/SKILL.md
|
|
70
|
+
uwf prompt bootstrap > ~/.hermes/skills/uwf/SKILL.md # bootstrap skill
|
|
71
|
+
\`\`\`
|
|
72
|
+
|
|
73
|
+
## Notes
|
|
74
|
+
|
|
75
|
+
- The skill content is bundled with the CLI and versioned with it — always use
|
|
76
|
+
\`uwf prompt usage\` to get the content matching your installed version.
|
|
77
|
+
- Do NOT hand-edit the skill body. If the CLI is updated, re-run \`uwf prompt setup\`
|
|
78
|
+
and follow the steps again.
|
|
79
|
+
- When upgrading, always delete the old skill first to avoid stale instructions.
|
|
80
|
+
`;
|
|
81
|
+
}
|