@aifabrix/builder 2.54.1 → 2.55.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/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +172 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +272 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/start.js +310 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/deploy.js +7 -4
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/offer-create-from-template.js +184 -0
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +8 -1
- package/lib/cli/setup-auth.js +11 -2
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +149 -0
- package/lib/commands/agent-kit.js +417 -0
- package/lib/commands/auth-status-user.js +99 -0
- package/lib/commands/auth-status.js +14 -4
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/datasource-load-export-cli.js +7 -2
- package/lib/commands/datasource-test-trust-cli.js +5 -1
- package/lib/commands/governance-command-action.js +5 -4
- package/lib/commands/identity-membership-role-cache.js +6 -1
- package/lib/commands/lifecycle-command-action.js +16 -15
- package/lib/commands/login-credentials.js +8 -3
- package/lib/commands/login-device.js +14 -5
- package/lib/commands/login.js +14 -1
- package/lib/commands/logout.js +50 -15
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +140 -59
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-governance-command-action.js +9 -7
- package/lib/commands/verify-operations-command-action.js +9 -8
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/commands/verify-trust-command-action.js +6 -5
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +8 -3
- package/lib/generator/helpers.js +12 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +232 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
- package/lib/role-assistant/knowledge-sync.js +9 -126
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +149 -31
- package/lib/role-assistant/test-cases-evidence.js +44 -5
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +297 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +53 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/infra-parameter.schema.json +51 -3
- package/lib/schema/infra.parameter.yaml +476 -40
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/schema/type/document-storage.json +1 -1
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/api-required-permissions.js +271 -0
- package/lib/utils/api.js +16 -13
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/cli-json-exit.js +41 -0
- package/lib/utils/cli-utils.js +23 -5
- package/lib/utils/controller-url.js +78 -4
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/deployment-errors.js +15 -0
- package/lib/utils/deployment-validation-helpers.js +4 -1
- package/lib/utils/device-code.js +31 -10
- package/lib/utils/error-formatters/error-parser.js +16 -1
- package/lib/utils/error-formatters/permission-errors.js +19 -5
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/keycloak-realm-ready.js +5 -2
- package/lib/utils/logger.js +1 -0
- package/lib/utils/paths.js +26 -5
- package/lib/utils/schema-loader.js +37 -0
- package/lib/utils/secrets-helpers.js +2 -2
- package/lib/utils/write-stream-and-wait.js +63 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +20 -6
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +65 -0
- package/templates/agent-kit/skills/shared/login.md +16 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -3
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +21 -2
- package/templates/applications/miso-controller/env.template +96 -25
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Start Claude Code or Codex in a named tmux session.
|
|
3
|
+
* @fileoverview Does not install Agent Kit skills
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const os = require('os');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const { execFileSync, spawnSync } = require('child_process');
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const { resolveGitRoot } = require('./repo-guard');
|
|
13
|
+
const { writeApproveAllFiles } = require('./host-approve');
|
|
14
|
+
|
|
15
|
+
const START_APPS = {
|
|
16
|
+
claude: { bin: 'claude' },
|
|
17
|
+
codex: { bin: 'codex' }
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {unknown} value
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function normalizeStartApp(value) {
|
|
25
|
+
const app = String(value || '').trim();
|
|
26
|
+
if (!START_APPS[app]) {
|
|
27
|
+
throw new Error('Invalid app. Use claude or codex.');
|
|
28
|
+
}
|
|
29
|
+
return app;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Session names may not contain colons or periods.
|
|
34
|
+
* @param {unknown} value
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function sanitizeSessionName(value) {
|
|
38
|
+
const name = String(value || '')
|
|
39
|
+
.trim()
|
|
40
|
+
.replace(/[.:\s]+/g, '-')
|
|
41
|
+
.replace(/-+/g, '-')
|
|
42
|
+
.replace(/^-|-$/g, '');
|
|
43
|
+
if (!name) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
'Invalid --name. Use the last folder of this git workspace, for example aifabrix-docs.'
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return name;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Default display name is the last folder of the git workspace.
|
|
53
|
+
* @param {unknown} explicit
|
|
54
|
+
* @param {string} cwd
|
|
55
|
+
* @returns {string}
|
|
56
|
+
*/
|
|
57
|
+
function resolveSessionName(explicit, cwd) {
|
|
58
|
+
const raw = explicit === undefined || explicit === null || explicit === ''
|
|
59
|
+
? path.basename(cwd)
|
|
60
|
+
: explicit;
|
|
61
|
+
return sanitizeSessionName(raw);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param {object} options
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
function currentUsername(options) {
|
|
69
|
+
if (options && options.username) {
|
|
70
|
+
return String(options.username);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return os.userInfo().username;
|
|
74
|
+
} catch {
|
|
75
|
+
return process.env.USER || process.env.LOGNAME || 'user';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Hostname + login, e.g. builder01-dev03.
|
|
81
|
+
* @param {object} [options]
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
function hostUserPrefix(options) {
|
|
85
|
+
const hostRaw = options && options.hostname ? options.hostname : os.hostname();
|
|
86
|
+
const host = String(hostRaw).split('.')[0];
|
|
87
|
+
return sanitizeSessionName(`${host}-${currentUsername(options || {})}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Remote Control session: builder01-dev03-aifabrix-docs.
|
|
92
|
+
* @param {string} prefix
|
|
93
|
+
* @param {string} session
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
function resolveRemoteControlName(prefix, session) {
|
|
97
|
+
return sanitizeSessionName(`${prefix}-${session}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @param {string} bin
|
|
102
|
+
* @param {Function} exec
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
function hasOnPath(bin, exec) {
|
|
106
|
+
try {
|
|
107
|
+
exec('which', [bin], {
|
|
108
|
+
encoding: 'utf8',
|
|
109
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
110
|
+
});
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {object} options
|
|
119
|
+
* @returns {void}
|
|
120
|
+
*/
|
|
121
|
+
function validateStartOptions(options) {
|
|
122
|
+
const app = String(options.app || '').trim();
|
|
123
|
+
if (options.remoteControl && app !== 'claude') {
|
|
124
|
+
throw new Error('--remote-control is only valid with claude.');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Claude argv after the binary (Remote Control names, not Claude --tmux).
|
|
130
|
+
* @param {object} p
|
|
131
|
+
* @returns {string[]}
|
|
132
|
+
*/
|
|
133
|
+
function claudeInnerArgs(p) {
|
|
134
|
+
const args = ['--name', p.session];
|
|
135
|
+
if (!p.remoteControl) {
|
|
136
|
+
return args;
|
|
137
|
+
}
|
|
138
|
+
return args.concat([
|
|
139
|
+
'--remote-control',
|
|
140
|
+
p.remoteControlName,
|
|
141
|
+
'--remote-control-session-name-prefix',
|
|
142
|
+
p.remoteControlPrefix
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @param {object} p
|
|
148
|
+
* @returns {object}
|
|
149
|
+
*/
|
|
150
|
+
function buildTmuxSpec(p) {
|
|
151
|
+
const spec = START_APPS[p.app];
|
|
152
|
+
const extra = p.app === 'claude' ? claudeInnerArgs(p) : [];
|
|
153
|
+
const mode = p.detach
|
|
154
|
+
? ['new-session', '-d', '-s', p.session, '-c', p.cwd]
|
|
155
|
+
: ['new-session', '-A', '-s', p.session, '-c', p.cwd];
|
|
156
|
+
const remote = p.app === 'claude' && p.remoteControl;
|
|
157
|
+
return {
|
|
158
|
+
bin: 'tmux',
|
|
159
|
+
args: [...mode, spec.bin, ...extra],
|
|
160
|
+
session: p.session,
|
|
161
|
+
appBin: spec.bin,
|
|
162
|
+
needTmux: true,
|
|
163
|
+
remoteControlName: remote ? p.remoteControlName : undefined,
|
|
164
|
+
remoteControlPrefix: remote ? p.remoteControlPrefix : undefined
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @param {object} p
|
|
170
|
+
* @returns {object}
|
|
171
|
+
*/
|
|
172
|
+
function buildLaunchSpec(p) {
|
|
173
|
+
return buildTmuxSpec(p);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {{ appBin: string, needTmux?: boolean }} spec
|
|
178
|
+
* @param {Function} exec
|
|
179
|
+
* @returns {void}
|
|
180
|
+
*/
|
|
181
|
+
function requireBinaries(spec, exec) {
|
|
182
|
+
if (spec.needTmux && !hasOnPath('tmux', exec)) {
|
|
183
|
+
throw new Error('tmux is not on PATH. Install tmux, then retry.');
|
|
184
|
+
}
|
|
185
|
+
if (hasOnPath(spec.appBin, exec)) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (spec.appBin === 'claude') {
|
|
189
|
+
throw new Error(
|
|
190
|
+
'claude is not on PATH. Install with: curl -fsSL https://claude.ai/install.sh | bash'
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
throw new Error('codex is not on PATH. Install with: npm install -g @openai/codex');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @param {object} options
|
|
198
|
+
* @returns {boolean}
|
|
199
|
+
*/
|
|
200
|
+
function shouldExecute(options) {
|
|
201
|
+
if (options.dryRun || options.json) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
if (options.detach) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
if (!process.stdout.isTTY) {
|
|
208
|
+
throw new Error('agent-kit start needs a TTY to attach. Pass --detach or --dry-run.');
|
|
209
|
+
}
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {object} options
|
|
215
|
+
* @param {{ bin: string, args: string[], cwd: string }} spec
|
|
216
|
+
* @returns {unknown}
|
|
217
|
+
*/
|
|
218
|
+
function spawnLaunch(options, spec) {
|
|
219
|
+
const spawn = options.spawnSync || spawnSync;
|
|
220
|
+
const result = spawn(spec.bin, spec.args, { stdio: 'inherit', cwd: spec.cwd });
|
|
221
|
+
const status = result && typeof result.status === 'number' ? result.status : 0;
|
|
222
|
+
if (status !== 0) {
|
|
223
|
+
throw new Error(`${spec.bin} exited with status ${status}`);
|
|
224
|
+
}
|
|
225
|
+
return status;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @param {object} spec
|
|
230
|
+
* @param {object} extra
|
|
231
|
+
* @returns {object}
|
|
232
|
+
*/
|
|
233
|
+
function toStartResult(spec, extra) {
|
|
234
|
+
const launch = [spec.bin, ...spec.args];
|
|
235
|
+
return {
|
|
236
|
+
command: 'start',
|
|
237
|
+
app: extra.app,
|
|
238
|
+
session: spec.session,
|
|
239
|
+
remoteControlName: spec.remoteControlName,
|
|
240
|
+
remoteControlPrefix: spec.remoteControlPrefix,
|
|
241
|
+
path: extra.cwd,
|
|
242
|
+
launch,
|
|
243
|
+
tmux: spec.needTmux ? launch : undefined,
|
|
244
|
+
approvals: extra.approvals,
|
|
245
|
+
dryRun: extra.dryRun,
|
|
246
|
+
detach: extra.detach,
|
|
247
|
+
executed: extra.executed
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {object} options
|
|
253
|
+
* @returns {object}
|
|
254
|
+
*/
|
|
255
|
+
function runStart(options) {
|
|
256
|
+
validateStartOptions(options);
|
|
257
|
+
const app = normalizeStartApp(options.app);
|
|
258
|
+
const cwd = resolveGitRoot(options.cwd || process.cwd());
|
|
259
|
+
const session = resolveSessionName(options.name, cwd);
|
|
260
|
+
const remoteControlPrefix = hostUserPrefix(options);
|
|
261
|
+
const remoteControlName = resolveRemoteControlName(remoteControlPrefix, session);
|
|
262
|
+
const exec = options.execFileSync || execFileSync;
|
|
263
|
+
const spec = buildLaunchSpec({
|
|
264
|
+
app,
|
|
265
|
+
cwd,
|
|
266
|
+
session,
|
|
267
|
+
remoteControlPrefix,
|
|
268
|
+
remoteControlName,
|
|
269
|
+
detach: Boolean(options.detach),
|
|
270
|
+
remoteControl: Boolean(options.remoteControl)
|
|
271
|
+
});
|
|
272
|
+
spec.cwd = cwd;
|
|
273
|
+
const approveAll = !options.skipApproveAll && Boolean(options.approveAll || options.yes);
|
|
274
|
+
const approvals = writeApproveAllFiles({
|
|
275
|
+
repoRoot: cwd,
|
|
276
|
+
hosts: [app],
|
|
277
|
+
approveAll,
|
|
278
|
+
dryRun: Boolean(options.dryRun)
|
|
279
|
+
});
|
|
280
|
+
if (!options.dryRun) {
|
|
281
|
+
requireBinaries(spec, exec);
|
|
282
|
+
}
|
|
283
|
+
const execute = shouldExecute(options);
|
|
284
|
+
if (execute && !options.json) {
|
|
285
|
+
logger.log(`Starting ${app} session ${spec.session}`);
|
|
286
|
+
}
|
|
287
|
+
if (execute) {
|
|
288
|
+
spawnLaunch(options, spec);
|
|
289
|
+
}
|
|
290
|
+
return toStartResult(spec, {
|
|
291
|
+
app,
|
|
292
|
+
cwd,
|
|
293
|
+
approvals,
|
|
294
|
+
dryRun: Boolean(options.dryRun),
|
|
295
|
+
detach: Boolean(options.detach),
|
|
296
|
+
executed: execute
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
module.exports = {
|
|
301
|
+
START_APPS,
|
|
302
|
+
normalizeStartApp,
|
|
303
|
+
sanitizeSessionName,
|
|
304
|
+
resolveSessionName,
|
|
305
|
+
hostUserPrefix,
|
|
306
|
+
resolveRemoteControlName,
|
|
307
|
+
claudeInnerArgs,
|
|
308
|
+
buildTmuxSpec,
|
|
309
|
+
runStart
|
|
310
|
+
};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installed Agent Kit state file.
|
|
3
|
+
* @fileoverview Read/write .aifabrix/agent-kit.yaml
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const yaml = require('js-yaml');
|
|
10
|
+
const fs = require('../internal/fs-real-sync');
|
|
11
|
+
const { STATE_RELATIVE, SECTION_ID } = require('./constants');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} repoRoot
|
|
15
|
+
* @returns {string}
|
|
16
|
+
*/
|
|
17
|
+
function statePath(repoRoot) {
|
|
18
|
+
return path.join(repoRoot, STATE_RELATIVE);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} repoRoot
|
|
23
|
+
* @returns {object|null}
|
|
24
|
+
*/
|
|
25
|
+
function loadState(repoRoot) {
|
|
26
|
+
const filePath = statePath(repoRoot);
|
|
27
|
+
if (!fs.existsSync(filePath)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
const parsed = yaml.load(fs.readFileSync(filePath, 'utf8'));
|
|
31
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {object} kit
|
|
39
|
+
* @param {string[]} hosts
|
|
40
|
+
* @param {object[]} ops
|
|
41
|
+
* @param {string} builderVersion
|
|
42
|
+
* @returns {object}
|
|
43
|
+
*/
|
|
44
|
+
function buildStateDocument(kit, hosts, ops, builderVersion) {
|
|
45
|
+
const managedFiles = [];
|
|
46
|
+
for (const op of ops) {
|
|
47
|
+
if (op.action === 'delete' || op.action === 'retain' || op.action === 'conflict') {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (op.action === 'skip' && op.fileState === 'MISSING') {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (!op.targetRel || op.mode === 'obsolete' || op.mode === 'if-missing') {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const row = { path: op.targetRel, sha256: op.sourceHash };
|
|
57
|
+
if (op.mode === 'managed-section') {
|
|
58
|
+
row.section = SECTION_ID;
|
|
59
|
+
}
|
|
60
|
+
managedFiles.push(row);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
schemaVersion: '1',
|
|
64
|
+
kitVersion: kit.manifest.kitVersion,
|
|
65
|
+
builderVersion,
|
|
66
|
+
installedAt: new Date().toISOString(),
|
|
67
|
+
hosts,
|
|
68
|
+
managedFiles
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} repoRoot
|
|
74
|
+
* @param {object} doc
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
function writeState(repoRoot, doc) {
|
|
78
|
+
const filePath = statePath(repoRoot);
|
|
79
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
80
|
+
fs.writeFileSync(filePath, yaml.dump(doc, { lineWidth: 120, noRefs: true, sortKeys: false }), 'utf8');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { statePath, loadState, buildStateDocument, writeState };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Kit status.
|
|
3
|
+
* @fileoverview Classify installed files without writing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const { getCliVersion } = require('../utils/installation-log-core');
|
|
9
|
+
const { resolveGitRoot } = require('./repo-guard');
|
|
10
|
+
const { loadKitPackage } = require('./package-load');
|
|
11
|
+
const { listManagedTargets } = require('./file-list');
|
|
12
|
+
const { classifyItems } = require('./classify');
|
|
13
|
+
const { detectV1, listObsoleteV1Items } = require('./v1-detect');
|
|
14
|
+
const { loadState } = require('./state-file');
|
|
15
|
+
const { ACTION } = require('./constants');
|
|
16
|
+
const { kitStateName, buildJsonReport } = require('./report');
|
|
17
|
+
const { ALL_HOSTS } = require('./hosts');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {object} options
|
|
21
|
+
* @returns {object}
|
|
22
|
+
*/
|
|
23
|
+
function runStatus(options) {
|
|
24
|
+
const repoRoot = resolveGitRoot(options.cwd || process.cwd());
|
|
25
|
+
const kit = loadKitPackage();
|
|
26
|
+
const state = loadState(repoRoot);
|
|
27
|
+
const includeShims = detectV1(repoRoot);
|
|
28
|
+
const hosts = (state && state.hosts) || ALL_HOSTS.slice();
|
|
29
|
+
const includeClaude = hosts.includes('claude');
|
|
30
|
+
const items = listManagedTargets(kit, repoRoot, {
|
|
31
|
+
includeShims: includeShims && hosts.includes('cursor'),
|
|
32
|
+
includeClaude
|
|
33
|
+
});
|
|
34
|
+
classifyItems(items, state, hosts);
|
|
35
|
+
items.push(...listObsoleteV1Items(kit, repoRoot, new Set(items.map((item) => item.targetRel))));
|
|
36
|
+
const ops = items.map((item) => ({ ...item, action: ACTION.SKIP }));
|
|
37
|
+
const kitState = kitStateName(state, kit, ops);
|
|
38
|
+
return buildJsonReport({
|
|
39
|
+
command: 'status',
|
|
40
|
+
kit,
|
|
41
|
+
state,
|
|
42
|
+
hosts,
|
|
43
|
+
ops,
|
|
44
|
+
dryRun: true,
|
|
45
|
+
builderVersion: getCliVersion(),
|
|
46
|
+
kitState
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = { runStatus };
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect v1 Agent Kit files in a customer repository.
|
|
3
|
+
* @fileoverview Name + hash detection for migration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('../internal/fs-real-sync');
|
|
10
|
+
const { V1_MARKERS, FILE_STATE } = require('./constants');
|
|
11
|
+
const { sha256File } = require('./checksums');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} repoRoot
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
function detectV1(repoRoot) {
|
|
18
|
+
return V1_MARKERS.some((rel) => fs.existsSync(path.join(repoRoot, rel)));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} kit
|
|
23
|
+
* @param {string} repoRoot
|
|
24
|
+
* @param {Set<string>} managedRels
|
|
25
|
+
* @returns {object[]}
|
|
26
|
+
*/
|
|
27
|
+
function listObsoleteV1Items(kit, repoRoot, managedRels) {
|
|
28
|
+
const rows = (kit.inventory && kit.inventory.v1Files) || [];
|
|
29
|
+
const items = [];
|
|
30
|
+
for (const row of rows) {
|
|
31
|
+
if (row.disposition === 'shim' || !row.installedPath) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (managedRels.has(row.installedPath)) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const targetAbs = path.join(repoRoot, row.installedPath);
|
|
38
|
+
if (!fs.existsSync(targetAbs)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const hash = sha256File(targetAbs);
|
|
42
|
+
const unchanged = hash === row.sha256;
|
|
43
|
+
items.push({
|
|
44
|
+
sourceAbs: null,
|
|
45
|
+
targetRel: row.installedPath,
|
|
46
|
+
targetAbs,
|
|
47
|
+
mode: 'obsolete',
|
|
48
|
+
sourceHash: row.sha256,
|
|
49
|
+
currentHash: hash,
|
|
50
|
+
fileState: unchanged
|
|
51
|
+
? FILE_STATE.OBSOLETE_UNCHANGED_MANAGED
|
|
52
|
+
: FILE_STATE.OBSOLETE_MODIFIED_MANAGED,
|
|
53
|
+
v1Owner: row.owner
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return items;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Replace unmodified v1 command files with thin shims on install.
|
|
61
|
+
* @param {object[]} items
|
|
62
|
+
* @param {object} kit
|
|
63
|
+
* @returns {void}
|
|
64
|
+
*/
|
|
65
|
+
function markV1ShimReplacements(items, kit) {
|
|
66
|
+
const byPath = new Map();
|
|
67
|
+
for (const row of (kit.inventory && kit.inventory.v1Files) || []) {
|
|
68
|
+
byPath.set(row.installedPath, row);
|
|
69
|
+
}
|
|
70
|
+
for (const item of items) {
|
|
71
|
+
if (!item.optional || !item.currentHash) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const row = byPath.get(item.targetRel);
|
|
75
|
+
if (row && row.disposition === 'shim' && item.currentHash === row.sha256) {
|
|
76
|
+
item.replaceOnInstall = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
module.exports = { detectV1, listObsoleteV1Items, markV1ShimReplacements };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate bundled Agent Kit package and optional install.
|
|
3
|
+
* @fileoverview Schema, paths, skill names, installed conflicts
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('../internal/fs-real-sync');
|
|
10
|
+
const { loadKitPackage } = require('./package-load');
|
|
11
|
+
const { listTreeFiles } = require('./file-list');
|
|
12
|
+
const { runStatus } = require('./status');
|
|
13
|
+
const { KIT_STATE } = require('./constants');
|
|
14
|
+
|
|
15
|
+
const FORBIDDEN_RE = /@[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}|kv:\/\/[^\s]+\/[A-Za-z0-9+/]{12,}|\/home\/[a-z0-9]+\/(?!dev)/i;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {object} kit
|
|
19
|
+
* @returns {string[]}
|
|
20
|
+
*/
|
|
21
|
+
function collectPackageErrors(kit) {
|
|
22
|
+
const errors = [];
|
|
23
|
+
const names = kit.manifest.skills || [];
|
|
24
|
+
if (new Set(names).size !== names.length) {
|
|
25
|
+
errors.push('Duplicate skill name in agent-kit.yaml.');
|
|
26
|
+
}
|
|
27
|
+
for (const name of names) {
|
|
28
|
+
const skillMd = path.join(kit.root, 'skills', name, 'SKILL.md');
|
|
29
|
+
if (!fs.existsSync(skillMd)) {
|
|
30
|
+
errors.push(`Missing SKILL.md for ${name}.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
for (const spec of [...(kit.manifest.managed || []), ...(kit.manifest.optional || [])]) {
|
|
34
|
+
const sourceAbs = path.join(kit.root, spec.source);
|
|
35
|
+
if (!fs.existsSync(sourceAbs)) {
|
|
36
|
+
errors.push(`Managed source missing: ${spec.source}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return errors;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {object} kit
|
|
44
|
+
* @returns {string[]}
|
|
45
|
+
*/
|
|
46
|
+
function collectContentErrors(kit) {
|
|
47
|
+
const errors = [];
|
|
48
|
+
for (const file of listTreeFiles(kit.root, '')) {
|
|
49
|
+
if (file.rel === 'parity-inventory.yaml') {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const text = fs.readFileSync(file.abs, 'utf8');
|
|
53
|
+
if (FORBIDDEN_RE.test(text)) {
|
|
54
|
+
errors.push(`Forbidden customer/secret pattern in ${file.rel}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return errors;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @param {object} [options]
|
|
62
|
+
* @returns {object}
|
|
63
|
+
*/
|
|
64
|
+
function runValidate(options = {}) {
|
|
65
|
+
const kit = loadKitPackage();
|
|
66
|
+
const errors = [...collectPackageErrors(kit), ...collectContentErrors(kit)];
|
|
67
|
+
let install = null;
|
|
68
|
+
try {
|
|
69
|
+
install = runStatus(options);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (!String(error.message || '').includes('git repository')) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (install && install.state === KIT_STATE.CONFLICTS) {
|
|
76
|
+
errors.push(`Install conflicts: ${install.conflicts.join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
success: errors.length === 0,
|
|
80
|
+
command: 'validate',
|
|
81
|
+
kitVersion: kit.manifest.kitVersion,
|
|
82
|
+
errors,
|
|
83
|
+
install
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { runValidate, collectPackageErrors, collectContentErrors };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve workspace directory name for agent-kit init.
|
|
3
|
+
* @fileoverview Name authority — never rewrite cust-* to aif-*
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string|undefined} nameOrPath
|
|
12
|
+
* @param {string} cwd
|
|
13
|
+
* @returns {{ targetAbs: string, workspaceName: string }}
|
|
14
|
+
*/
|
|
15
|
+
function resolveWorkspaceTarget(nameOrPath, cwd) {
|
|
16
|
+
const base = path.resolve(cwd || process.cwd());
|
|
17
|
+
if (!nameOrPath || !String(nameOrPath).trim()) {
|
|
18
|
+
throw new Error('Workspace name or path is required. Example: aifabrix agent-kit init cust-demo');
|
|
19
|
+
}
|
|
20
|
+
const raw = String(nameOrPath).trim();
|
|
21
|
+
const looksLikePath =
|
|
22
|
+
path.isAbsolute(raw) || raw.includes('/') || raw.includes('\\') || raw === '.' || raw.startsWith('.');
|
|
23
|
+
const targetAbs = looksLikePath ? path.resolve(base, raw) : path.join(base, raw);
|
|
24
|
+
const workspaceName = path.basename(targetAbs);
|
|
25
|
+
if (!workspaceName || workspaceName === '.' || workspaceName === '..') {
|
|
26
|
+
throw new Error('Could not derive a workspace name from the path.');
|
|
27
|
+
}
|
|
28
|
+
return { targetAbs, workspaceName };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Preserve user-supplied names; never rewrite cust-* prefixes.
|
|
33
|
+
* @param {string} name
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
36
|
+
function assertNameUnchanged(name) {
|
|
37
|
+
return name;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { resolveWorkspaceTarget, assertNameUnchanged };
|