@dev-mainsequence/codex-plugin 0.0.0-stage → 0.4.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/.codex-plugin/plugin.json +22 -0
- package/.mcp.json +16 -0
- package/LICENSE +21 -0
- package/NOTICE +13 -0
- package/README.md +155 -2
- package/assets/mainsequence-icon.png +0 -0
- package/assets/mainsequence-icon.svg +454 -0
- package/build-info.json +10 -0
- package/dist/hosts/codex.js +62 -0
- package/dist/hosts/codexPresentation.js +165 -0
- package/dist/hosts/discovery.js +138 -0
- package/dist/hosts/panel/panel.html +165 -0
- package/dist/package.json +1 -0
- package/dist/panel-server.js +135 -0
- package/dist/runner.js +71 -0
- package/dist/vendor/devtools/LICENSE +21 -0
- package/dist/vendor/devtools/NOTICE +12 -0
- package/dist/vendor/devtools/index.js +72 -0
- package/dist/vendor/devtools/package.json +5 -0
- package/dist/vendor/devtools/shared/accountScope.js +19 -0
- package/dist/vendor/devtools/shared/actions.js +164 -0
- package/dist/vendor/devtools/shared/agentReadiness.js +71 -0
- package/dist/vendor/devtools/shared/agents.js +240 -0
- package/dist/vendor/devtools/shared/arguments.js +142 -0
- package/dist/vendor/devtools/shared/cli.js +144 -0
- package/dist/vendor/devtools/shared/config.js +64 -0
- package/dist/vendor/devtools/shared/devcontainerJsonc.js +153 -0
- package/dist/vendor/devtools/shared/envfile.js +128 -0
- package/dist/vendor/devtools/shared/environments.js +38 -0
- package/dist/vendor/devtools/shared/hostSession.js +324 -0
- package/dist/vendor/devtools/shared/machineSession.js +338 -0
- package/dist/vendor/devtools/shared/mcp.js +421 -0
- package/dist/vendor/devtools/shared/mcpTransport.js +177 -0
- package/dist/vendor/devtools/shared/models.js +12 -0
- package/dist/vendor/devtools/shared/organizationProjectWorkspace.js +300 -0
- package/dist/vendor/devtools/shared/organizationProjects.js +134 -0
- package/dist/vendor/devtools/shared/process.js +61 -0
- package/dist/vendor/devtools/shared/projectOperations.js +279 -0
- package/dist/vendor/devtools/shared/projects.js +390 -0
- package/dist/vendor/devtools/shared/pythonProjectStatus.js +86 -0
- package/dist/vendor/devtools/shared/pythonSdkStatus.js +26 -0
- package/dist/vendor/devtools/shared/repositories.js +215 -0
- package/dist/vendor/devtools/shared/repositoryClient.js +108 -0
- package/dist/vendor/devtools/shared/repositoryIdentity.js +116 -0
- package/dist/vendor/devtools/shared/repositoryModels.js +72 -0
- package/dist/vendor/devtools/shared/session.js +532 -0
- package/docs/adr/0002-conversational-project-mcp-client.md +59 -0
- package/docs/agents-tools-panel.md +63 -0
- package/docs/codex-verification.md +161 -0
- package/docs/conversational-mcp.md +54 -0
- package/hooks/hooks.json +17 -0
- package/package.json +50 -4
- package/skills/mainsequence-mcp/SKILL.md +40 -0
- package/skills/ms/SKILL.md +8 -0
- package/skills/ms/agents/openai.yaml +7 -0
- package/skills/ms/assets/mainsequence-icon.png +0 -0
- package/skills/ms/assets/mainsequence-icon.svg +454 -0
package/dist/runner.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
// The plugin's own entry point: `--codex-hook` serves the UserPromptSubmit hook from
|
|
5
|
+
// stdin; any other invocation is the plain ms-devtools terminal CLI, so this package's
|
|
6
|
+
// bin keeps working in a shell.
|
|
7
|
+
const devtools_1 = require("./vendor/devtools/index.js");
|
|
8
|
+
const codex_1 = require("./hosts/codex.js");
|
|
9
|
+
async function main() {
|
|
10
|
+
const argv = process.argv.slice(2);
|
|
11
|
+
const hook = argv.length === 1 && argv[0] === '--codex-hook';
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const stop = () => controller.abort();
|
|
14
|
+
process.once('SIGINT', stop);
|
|
15
|
+
process.once('SIGTERM', stop);
|
|
16
|
+
// Finish a handled prompt before Codex's 600-second hook limit.
|
|
17
|
+
const timer = hook ? setTimeout(stop, 550_000) : undefined;
|
|
18
|
+
// The Codex hook reports only its final blocking result. Sync is local and needs no backend preflight.
|
|
19
|
+
const tooling = (0, devtools_1.createDevTools)({ host: (0, devtools_1.createNodeHost)(hook || argv.includes('--json') ? undefined : message => process.stderr.write(message + '\n')), signal: controller.signal });
|
|
20
|
+
try {
|
|
21
|
+
if (hook) {
|
|
22
|
+
let stdin = '';
|
|
23
|
+
for await (const chunk of process.stdin) {
|
|
24
|
+
stdin += chunk;
|
|
25
|
+
if (stdin.length > 1_000_000)
|
|
26
|
+
throw new devtools_1.ToolingError('INVALID_REQUEST', 'Hook input is too large.');
|
|
27
|
+
}
|
|
28
|
+
let input;
|
|
29
|
+
try {
|
|
30
|
+
input = JSON.parse(stdin);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const output = await (0, codex_1.handleCodexPrompt)(input, tooling.dispatch);
|
|
36
|
+
if (output)
|
|
37
|
+
process.stdout.write(JSON.stringify(output) + '\n');
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let result;
|
|
41
|
+
try {
|
|
42
|
+
const parsed = (0, devtools_1.parseArguments)(argv);
|
|
43
|
+
if (parsed.help) {
|
|
44
|
+
process.stdout.write(devtools_1.HELP);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
result = await tooling.dispatch(parsed.request);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof devtools_1.ToolingError ? error.message : 'The command could not be parsed.';
|
|
51
|
+
result = { schemaVersion: 1, action: 'invalid', status: 'failure', summary: message, error: { code: 'INVALID_REQUEST', message } };
|
|
52
|
+
}
|
|
53
|
+
process.stdout.write(argv.includes('--json') ? JSON.stringify(result) + '\n' : result.summary + '\n' + (result.data === undefined ? '' : JSON.stringify(result.data, null, 2) + '\n'));
|
|
54
|
+
process.exitCode = result.status === 'success' ? 0 : result.status === 'cancelled' ? 130 : 1;
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (timer)
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
process.removeListener('SIGINT', stop);
|
|
60
|
+
process.removeListener('SIGTERM', stop);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
void main().catch(() => {
|
|
64
|
+
// A handled-hook failure must block, never pass through to model inference.
|
|
65
|
+
if (process.argv.includes('--codex-hook'))
|
|
66
|
+
process.stdout.write(JSON.stringify({ decision: 'block', reason: 'Developer action runner failed.' }) + '\n');
|
|
67
|
+
else {
|
|
68
|
+
process.stderr.write('Developer action runner failed.\n');
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 MainSequence SDK
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
The native machine-session store, backend normalization, browser authorization flow,
|
|
2
|
+
repository identity/layout, response models, and project CLI contracts derive from
|
|
3
|
+
ms-vscode-extension 0.1.45 (4bb107e), Copyright (c) 2024 MainSequence SDK, MIT license.
|
|
4
|
+
|
|
5
|
+
Adaptations remove VS Code APIs and legacy project-token projection, add explicit host
|
|
6
|
+
interfaces, cancellation, safe results, and deterministic command dispatch.
|
|
7
|
+
The SDK repositories remain independent and are not bundled.
|
|
8
|
+
|
|
9
|
+
MainSequence brand icons are copied unchanged from the public website:
|
|
10
|
+
https://www.main-sequence.io/favicon.svg
|
|
11
|
+
https://www.main-sequence.io/favicon-192.png
|
|
12
|
+
Retrieved 2026-10-01. The branding belongs to MainSequence.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HELP = exports.tokenizeCommand = exports.parseArguments = exports.readOrganizationProjectWorkspace = exports.OrganizationProjectClient = exports.updateDevcontainerJsonc = exports.validateDockerPlatform = exports.validateDockerImage = exports.PROJECT_OPERATION_ACTIONS = exports.createProjectOperations = exports.AgentClient = exports.bindSessionAccount = exports.listEnvironments = exports.PYTHON_SDK_PROJECT_URL = exports.readPythonProjectSdkStatus = exports.projectVersion = exports.hasPythonProject = exports.pythonExecutable = exports.sanitizeCommitMessage = exports.codeRepositoryActionPreview = exports.buildCodeRepositoryCliInvocations = exports.resolveCodeRepositoryCli = exports.repositorySshKeyPaths = exports.repositorySshKeyName = exports.repositorySshKeyIdentity = exports.RepositoryClient = exports.safeSlug = exports.canonicalRepositoryDir = exports.detectProjectType = exports.projectContext = exports.readWorkspacePreferences = exports.normalizeBackendUrl = exports.ForeignEntryError = exports.CredentialStoreError = exports.SESSION_SERVICE = exports.saveSession = exports.readSession = exports.clearSession = exports.authSecretKey = exports.HostAuthSession = exports.createSessionService = exports.ToolingError = exports.openCredentialStore = exports.sessionAccount = exports.requireBackend = exports.createNodeHost = exports.createDevTools = void 0;
|
|
4
|
+
var actions_1 = require("./shared/actions.js");
|
|
5
|
+
Object.defineProperty(exports, "createDevTools", { enumerable: true, get: function () { return actions_1.createDevTools; } });
|
|
6
|
+
var process_1 = require("./shared/process.js");
|
|
7
|
+
Object.defineProperty(exports, "createNodeHost", { enumerable: true, get: function () { return process_1.createNodeHost; } });
|
|
8
|
+
var session_1 = require("./shared/session.js");
|
|
9
|
+
Object.defineProperty(exports, "requireBackend", { enumerable: true, get: function () { return session_1.requireBackend; } });
|
|
10
|
+
var machineSession_1 = require("./shared/machineSession.js");
|
|
11
|
+
Object.defineProperty(exports, "sessionAccount", { enumerable: true, get: function () { return machineSession_1.sessionAccount; } });
|
|
12
|
+
Object.defineProperty(exports, "openCredentialStore", { enumerable: true, get: function () { return machineSession_1.openCredentialStore; } });
|
|
13
|
+
var models_1 = require("./shared/models.js");
|
|
14
|
+
Object.defineProperty(exports, "ToolingError", { enumerable: true, get: function () { return models_1.ToolingError; } });
|
|
15
|
+
var session_2 = require("./shared/session.js");
|
|
16
|
+
Object.defineProperty(exports, "createSessionService", { enumerable: true, get: function () { return session_2.createSessionService; } });
|
|
17
|
+
var hostSession_1 = require("./shared/hostSession.js");
|
|
18
|
+
Object.defineProperty(exports, "HostAuthSession", { enumerable: true, get: function () { return hostSession_1.HostAuthSession; } });
|
|
19
|
+
Object.defineProperty(exports, "authSecretKey", { enumerable: true, get: function () { return hostSession_1.authSecretKey; } });
|
|
20
|
+
var machineSession_2 = require("./shared/machineSession.js");
|
|
21
|
+
Object.defineProperty(exports, "clearSession", { enumerable: true, get: function () { return machineSession_2.clearSession; } });
|
|
22
|
+
Object.defineProperty(exports, "readSession", { enumerable: true, get: function () { return machineSession_2.readSession; } });
|
|
23
|
+
Object.defineProperty(exports, "saveSession", { enumerable: true, get: function () { return machineSession_2.saveSession; } });
|
|
24
|
+
Object.defineProperty(exports, "SESSION_SERVICE", { enumerable: true, get: function () { return machineSession_2.SESSION_SERVICE; } });
|
|
25
|
+
Object.defineProperty(exports, "CredentialStoreError", { enumerable: true, get: function () { return machineSession_2.CredentialStoreError; } });
|
|
26
|
+
Object.defineProperty(exports, "ForeignEntryError", { enumerable: true, get: function () { return machineSession_2.ForeignEntryError; } });
|
|
27
|
+
var config_1 = require("./shared/config.js");
|
|
28
|
+
Object.defineProperty(exports, "normalizeBackendUrl", { enumerable: true, get: function () { return config_1.normalizeBackendUrl; } });
|
|
29
|
+
var repositories_1 = require("./shared/repositories.js");
|
|
30
|
+
Object.defineProperty(exports, "readWorkspacePreferences", { enumerable: true, get: function () { return repositories_1.readWorkspacePreferences; } });
|
|
31
|
+
Object.defineProperty(exports, "projectContext", { enumerable: true, get: function () { return repositories_1.projectContext; } });
|
|
32
|
+
Object.defineProperty(exports, "detectProjectType", { enumerable: true, get: function () { return repositories_1.detectProjectType; } });
|
|
33
|
+
Object.defineProperty(exports, "canonicalRepositoryDir", { enumerable: true, get: function () { return repositories_1.canonicalRepositoryDir; } });
|
|
34
|
+
Object.defineProperty(exports, "safeSlug", { enumerable: true, get: function () { return repositories_1.safeSlug; } });
|
|
35
|
+
var repositoryClient_1 = require("./shared/repositoryClient.js");
|
|
36
|
+
Object.defineProperty(exports, "RepositoryClient", { enumerable: true, get: function () { return repositoryClient_1.RepositoryClient; } });
|
|
37
|
+
var repositoryIdentity_1 = require("./shared/repositoryIdentity.js");
|
|
38
|
+
Object.defineProperty(exports, "repositorySshKeyIdentity", { enumerable: true, get: function () { return repositoryIdentity_1.repositorySshKeyIdentity; } });
|
|
39
|
+
Object.defineProperty(exports, "repositorySshKeyName", { enumerable: true, get: function () { return repositoryIdentity_1.repositorySshKeyName; } });
|
|
40
|
+
Object.defineProperty(exports, "repositorySshKeyPaths", { enumerable: true, get: function () { return repositoryIdentity_1.repositorySshKeyPaths; } });
|
|
41
|
+
var cli_1 = require("./shared/cli.js");
|
|
42
|
+
Object.defineProperty(exports, "resolveCodeRepositoryCli", { enumerable: true, get: function () { return cli_1.resolveCodeRepositoryCli; } });
|
|
43
|
+
Object.defineProperty(exports, "buildCodeRepositoryCliInvocations", { enumerable: true, get: function () { return cli_1.buildCodeRepositoryCliInvocations; } });
|
|
44
|
+
Object.defineProperty(exports, "codeRepositoryActionPreview", { enumerable: true, get: function () { return cli_1.codeRepositoryActionPreview; } });
|
|
45
|
+
Object.defineProperty(exports, "sanitizeCommitMessage", { enumerable: true, get: function () { return cli_1.sanitizeCommitMessage; } });
|
|
46
|
+
var pythonProjectStatus_1 = require("./shared/pythonProjectStatus.js");
|
|
47
|
+
Object.defineProperty(exports, "pythonExecutable", { enumerable: true, get: function () { return pythonProjectStatus_1.pythonExecutable; } });
|
|
48
|
+
Object.defineProperty(exports, "hasPythonProject", { enumerable: true, get: function () { return pythonProjectStatus_1.hasPythonProject; } });
|
|
49
|
+
Object.defineProperty(exports, "projectVersion", { enumerable: true, get: function () { return pythonProjectStatus_1.projectVersion; } });
|
|
50
|
+
Object.defineProperty(exports, "readPythonProjectSdkStatus", { enumerable: true, get: function () { return pythonProjectStatus_1.readPythonSdkStatus; } });
|
|
51
|
+
Object.defineProperty(exports, "PYTHON_SDK_PROJECT_URL", { enumerable: true, get: function () { return pythonProjectStatus_1.PYTHON_SDK_PROJECT_URL; } });
|
|
52
|
+
var environments_1 = require("./shared/environments.js");
|
|
53
|
+
Object.defineProperty(exports, "listEnvironments", { enumerable: true, get: function () { return environments_1.listEnvironments; } });
|
|
54
|
+
var accountScope_1 = require("./shared/accountScope.js");
|
|
55
|
+
Object.defineProperty(exports, "bindSessionAccount", { enumerable: true, get: function () { return accountScope_1.bindSessionAccount; } });
|
|
56
|
+
var agents_1 = require("./shared/agents.js");
|
|
57
|
+
Object.defineProperty(exports, "AgentClient", { enumerable: true, get: function () { return agents_1.AgentClient; } });
|
|
58
|
+
var projectOperations_1 = require("./shared/projectOperations.js");
|
|
59
|
+
Object.defineProperty(exports, "createProjectOperations", { enumerable: true, get: function () { return projectOperations_1.createProjectOperations; } });
|
|
60
|
+
Object.defineProperty(exports, "PROJECT_OPERATION_ACTIONS", { enumerable: true, get: function () { return projectOperations_1.PROJECT_OPERATION_ACTIONS; } });
|
|
61
|
+
Object.defineProperty(exports, "validateDockerImage", { enumerable: true, get: function () { return projectOperations_1.validateDockerImage; } });
|
|
62
|
+
Object.defineProperty(exports, "validateDockerPlatform", { enumerable: true, get: function () { return projectOperations_1.validateDockerPlatform; } });
|
|
63
|
+
var devcontainerJsonc_1 = require("./shared/devcontainerJsonc.js");
|
|
64
|
+
Object.defineProperty(exports, "updateDevcontainerJsonc", { enumerable: true, get: function () { return devcontainerJsonc_1.updateDevcontainerJsonc; } });
|
|
65
|
+
var organizationProjects_1 = require("./shared/organizationProjects.js");
|
|
66
|
+
Object.defineProperty(exports, "OrganizationProjectClient", { enumerable: true, get: function () { return organizationProjects_1.OrganizationProjectClient; } });
|
|
67
|
+
var organizationProjectWorkspace_1 = require("./shared/organizationProjectWorkspace.js");
|
|
68
|
+
Object.defineProperty(exports, "readOrganizationProjectWorkspace", { enumerable: true, get: function () { return organizationProjectWorkspace_1.readOrganizationProjectWorkspace; } });
|
|
69
|
+
var arguments_1 = require("./shared/arguments.js");
|
|
70
|
+
Object.defineProperty(exports, "parseArguments", { enumerable: true, get: function () { return arguments_1.parseArguments; } });
|
|
71
|
+
Object.defineProperty(exports, "tokenizeCommand", { enumerable: true, get: function () { return arguments_1.tokenizeCommand; } });
|
|
72
|
+
Object.defineProperty(exports, "HELP", { enumerable: true, get: function () { return arguments_1.HELP; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateExpectedAccount = validateExpectedAccount;
|
|
4
|
+
exports.bindSessionAccount = bindSessionAccount;
|
|
5
|
+
const models_1 = require("./models.js");
|
|
6
|
+
function validateExpectedAccount(value) {
|
|
7
|
+
if (typeof value !== 'string' || !value.trim() || value.length > 300 || /[\r\n\0]/.test(value)) {
|
|
8
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Use the account returned by verified session status.');
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** Opt-in account scope for domain operations; never silently weaken a custom transport. */
|
|
12
|
+
async function bindSessionAccount(session, backend, expectedAccount) {
|
|
13
|
+
if (expectedAccount === undefined)
|
|
14
|
+
return session;
|
|
15
|
+
validateExpectedAccount(expectedAccount);
|
|
16
|
+
if (!session.withAccount)
|
|
17
|
+
throw new models_1.ToolingError('UNSUPPORTED_ACTION', 'This session transport does not support account-bound operations. Upgrade its session implementation.');
|
|
18
|
+
return session.withAccount(backend, expectedAccount);
|
|
19
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDevTools = createDevTools;
|
|
4
|
+
exports.validateRequest = validateRequest;
|
|
5
|
+
const mcp_1 = require("./mcp.js");
|
|
6
|
+
const agents_1 = require("./agents.js");
|
|
7
|
+
const models_1 = require("./models.js");
|
|
8
|
+
const session_1 = require("./session.js");
|
|
9
|
+
const repositories_1 = require("./repositories.js");
|
|
10
|
+
const projects_1 = require("./projects.js");
|
|
11
|
+
const organizationProjects_1 = require("./organizationProjects.js");
|
|
12
|
+
const organizationProjectWorkspace_1 = require("./organizationProjectWorkspace.js");
|
|
13
|
+
const accountScope_1 = require("./accountScope.js");
|
|
14
|
+
function createDevTools(options) {
|
|
15
|
+
const { host, session: suppliedSession, allowMcpSkills, allowLegacyProjectCredentials, mcpConnectionsPath, ...sessionOptions } = options;
|
|
16
|
+
const session = suppliedSession ?? new session_1.MachineSessionService(host, sessionOptions);
|
|
17
|
+
const env = { ...(options.env ?? process.env) };
|
|
18
|
+
async function dispatch(request) {
|
|
19
|
+
const state = { mutationStarted: false };
|
|
20
|
+
const action = request?.action || 'unknown';
|
|
21
|
+
try {
|
|
22
|
+
validateRequest(request);
|
|
23
|
+
options.signal?.throwIfAborted();
|
|
24
|
+
if (action.startsWith('organization-')) {
|
|
25
|
+
const input = request;
|
|
26
|
+
const backend = await (0, repositories_1.resolveBackend)(request.backend, undefined, env);
|
|
27
|
+
const scoped = await (0, accountScope_1.bindSessionAccount)(session, backend, input.expectedAccount);
|
|
28
|
+
const client = new organizationProjects_1.OrganizationProjectClient(scoped, backend, options.signal);
|
|
29
|
+
let data;
|
|
30
|
+
if (input.action === 'organization-environments')
|
|
31
|
+
data = await client.environments();
|
|
32
|
+
else if (input.action === 'organization-projects')
|
|
33
|
+
data = await client.list(input.environmentUid, input);
|
|
34
|
+
else if (input.action === 'organization-project')
|
|
35
|
+
data = await client.getProject(input.environmentUid, input.projectUid);
|
|
36
|
+
else if (input.action === 'organization-project-link') {
|
|
37
|
+
const url = await client.link(input);
|
|
38
|
+
if (input.expectedAccount !== undefined)
|
|
39
|
+
await scoped.status(backend, true);
|
|
40
|
+
if (!await host.openBrowser(url))
|
|
41
|
+
throw new models_1.ToolingError('BROWSER_FAILED', 'The resource link could not open.');
|
|
42
|
+
data = { url };
|
|
43
|
+
}
|
|
44
|
+
else if (input.action === 'organization-project-setup')
|
|
45
|
+
data = await (0, organizationProjectWorkspace_1.setupOrganizationProject)({ ...input, backend }, { host, session: scoped, env, signal: options.signal, state });
|
|
46
|
+
else
|
|
47
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Unknown organization project action.');
|
|
48
|
+
if (input.expectedAccount !== undefined)
|
|
49
|
+
await scoped.status(backend, true);
|
|
50
|
+
return safeResult({ schemaVersion: 1, action, status: 'success', summary: input.action === 'organization-project-setup' ? 'Project checkouts are prepared on their assigned branches. Claude must attach them when its workspace session starts.' : 'Projects loaded.', data });
|
|
51
|
+
}
|
|
52
|
+
if ((0, agents_1.isAgentAction)(action)) {
|
|
53
|
+
const backend = await (0, repositories_1.resolveBackend)(request.backend, undefined, env);
|
|
54
|
+
return safeResult(await new agents_1.AgentClient(session, { signal: options.signal, progress: host.progress?.bind(host) }).dispatch(request, backend));
|
|
55
|
+
}
|
|
56
|
+
if (action.startsWith('mcp-')) {
|
|
57
|
+
const backend = await (0, repositories_1.resolveBackend)(request.backend, process.cwd(), env);
|
|
58
|
+
const input = request, scoped = await (0, accountScope_1.bindSessionAccount)(session, backend, input.expectedAccount);
|
|
59
|
+
return safeResult(await new mcp_1.McpConnections(scoped, { connectionsPath: mcpConnectionsPath, env, fetch: options.fetch, signal: options.signal, requestTimeoutMs: options.requestTimeoutMs,
|
|
60
|
+
checkAccount: input.expectedAccount === undefined ? undefined : () => scoped.status(backend, true).then(() => { }) }).dispatch(input, backend));
|
|
61
|
+
}
|
|
62
|
+
let data;
|
|
63
|
+
if (['login', 'logout', 'refresh-token', 'auth-status', 'repositories'].includes(action)) {
|
|
64
|
+
const backend = await (0, repositories_1.resolveBackend)(request.backend, undefined, env);
|
|
65
|
+
if (request.action === 'login')
|
|
66
|
+
data = await session.login(backend);
|
|
67
|
+
else if (request.action === 'logout')
|
|
68
|
+
data = await session.logout(backend);
|
|
69
|
+
else if (request.action === 'refresh-token')
|
|
70
|
+
data = await session.refresh(backend);
|
|
71
|
+
else if (request.action === 'auth-status')
|
|
72
|
+
data = await session.status(backend, request.check);
|
|
73
|
+
else
|
|
74
|
+
data = await (0, repositories_1.listRepositories)(session, backend);
|
|
75
|
+
}
|
|
76
|
+
else if (request.action === 'setup') {
|
|
77
|
+
data = await (0, projects_1.setupRepository)(host, session, { ...request, backend: await (0, repositories_1.resolveBackend)(request.backend, undefined, env) }, { env, signal: options.signal, state });
|
|
78
|
+
}
|
|
79
|
+
else if ('root' in request) {
|
|
80
|
+
const context = await (0, repositories_1.projectContext)(host, request.root, request.backend, request.projectType, env, options.signal);
|
|
81
|
+
if (request.action === 'status')
|
|
82
|
+
data = { project: context, capabilities: await (0, projects_1.projectCapabilities)(host, context, env, options.signal, allowMcpSkills), session: await session.status(context.backend) };
|
|
83
|
+
else if (request.action === 'resolve')
|
|
84
|
+
data = { project: context, resolution: await (0, repositories_1.resolveRepository)(session, context) };
|
|
85
|
+
else {
|
|
86
|
+
const operation = () => (0, projects_1.runProjectAction)(host, session, context, request.action, { ...request, env, signal: options.signal, state, fetch: options.fetch, allowMcpSkills, allowLegacyProjectCredentials });
|
|
87
|
+
data = ['sdk-status', 'open-folder', 'signed-terminal'].includes(action) || ('dryRun' in request && request.dryRun)
|
|
88
|
+
? await operation() : await (0, projects_1.withProjectLock)(context.gitCommonDir, operation);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const summary = action === 'auth-status' ? (data.authenticated ? 'The selected session is usable.' : 'No usable session is selected.')
|
|
92
|
+
: action === 'login' ? 'Signed in; the machine session was saved and verified.'
|
|
93
|
+
: action === 'refresh-token' ? 'The saved machine session was renewed.'
|
|
94
|
+
: action === 'logout' ? (data.revoked ? 'The machine session was revoked and removed.' : 'The local machine session was removed; remote revocation was not confirmed.')
|
|
95
|
+
: action === 'sync' ? 'Dependency files were refreshed. Nothing was committed or pushed: review the changes, then commit and push them yourself.'
|
|
96
|
+
: `${action} completed.`;
|
|
97
|
+
return safeResult({ schemaVersion: 1, action, status: 'success', summary, data });
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const failure = options.signal?.aborted ? new models_1.ToolingError('CANCELLED', 'The action was cancelled.')
|
|
101
|
+
: error instanceof models_1.ToolingError ? error : new models_1.ToolingError('INTERNAL_ERROR', 'The action could not complete. Check the required local tools and project permissions.');
|
|
102
|
+
return safeResult({ schemaVersion: 1, action, status: failure.code === 'CANCELLED' ? 'cancelled' : 'failure', summary: failure.message,
|
|
103
|
+
error: { code: failure.code, message: failure.message },
|
|
104
|
+
...(state.mutationStarted ? { effects: { state: 'partial-or-uncertain', message: 'Files, dependencies, keys, or remote state may have changed. Inspect the result before retrying; no automatic rollback was attempted.' } } : {})
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function safeResult(result) {
|
|
109
|
+
// The dispatcher is the one public output boundary for every host.
|
|
110
|
+
const sanitize = (value) => {
|
|
111
|
+
if (typeof value === 'string')
|
|
112
|
+
return session.sanitize?.(value) ?? value;
|
|
113
|
+
if (Array.isArray(value))
|
|
114
|
+
return value.map(sanitize);
|
|
115
|
+
if (value && typeof value === 'object')
|
|
116
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, sanitize(item)]));
|
|
117
|
+
return value;
|
|
118
|
+
};
|
|
119
|
+
return sanitize(result);
|
|
120
|
+
}
|
|
121
|
+
return { dispatch };
|
|
122
|
+
}
|
|
123
|
+
const ACTIONS = new Set(['login', 'logout', 'refresh-token', 'auth-status', 'repositories', 'status', 'resolve', 'setup', 'sync', 'sdk-status', 'update-sdk', 'update-agents', 'install-skills', 'skills-sync', 'install', 'env-cleanup', 'open-folder', 'signed-terminal']);
|
|
124
|
+
// Removed actions keep a specific answer, so that an older host learns what replaced them.
|
|
125
|
+
const RETIRED_ACTIONS = {
|
|
126
|
+
'freeze-env': 'freeze-env was removed: sync now refreshes the lock file, the environment and requirements.txt. Run sync instead.',
|
|
127
|
+
};
|
|
128
|
+
function validateRequest(request) {
|
|
129
|
+
if (request && ['organization-environments', 'organization-projects', 'organization-project', 'organization-project-link', 'organization-project-setup'].includes(request.action)) {
|
|
130
|
+
if (request.backend !== undefined && typeof request.backend !== 'string')
|
|
131
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Backend must be a URL string.');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (request && (0, agents_1.isAgentAction)(request.action)) {
|
|
135
|
+
if (request.backend !== undefined && typeof request.backend !== 'string')
|
|
136
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Backend must be a URL string.');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (request && ['mcp-environments', 'mcp-discover', 'mcp-add', 'mcp-list', 'mcp-remove', 'mcp-tools', 'mcp-call'].includes(request.action)) {
|
|
140
|
+
if (request.backend !== undefined && typeof request.backend !== 'string')
|
|
141
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Backend must be a URL string.');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (request && typeof request.action === 'string' && Object.hasOwn(RETIRED_ACTIONS, request.action))
|
|
145
|
+
throw new models_1.ToolingError('UNSUPPORTED_ACTION', RETIRED_ACTIONS[request.action]);
|
|
146
|
+
if (!request || !ACTIONS.has(request.action))
|
|
147
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Unknown developer action. Run --help for supported commands.');
|
|
148
|
+
if (!['login', 'logout', 'refresh-token', 'auth-status', 'repositories', 'setup'].includes(request.action) && !('root' in request))
|
|
149
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'A project root is required.');
|
|
150
|
+
if ('root' in request && (typeof request.root !== 'string' || !request.root.trim() || request.root.includes('\0')))
|
|
151
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'A project root is required.');
|
|
152
|
+
if (request.backend !== undefined && typeof request.backend !== 'string')
|
|
153
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Backend must be a URL string.');
|
|
154
|
+
if ('projectType' in request && request.projectType && !['python', 'vite_react'].includes(request.projectType))
|
|
155
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Project type must be python or vite_react.');
|
|
156
|
+
// A sync request from an older host may still carry a commit message. Sync ignores it.
|
|
157
|
+
if ('dryRun' in request && request.dryRun && !projects_1.DRY_RUN_ACTIONS.includes(request.action))
|
|
158
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'This action has no dry-run mode.');
|
|
159
|
+
if (request.action === 'setup') {
|
|
160
|
+
for (const value of [request.repositoryUid, request.branch, request.baseDir])
|
|
161
|
+
if (typeof value !== 'string' || !value.trim() || /[\r\n\0]/.test(value))
|
|
162
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Setup requires --uid, --branch, and --base-dir.');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.waitForAgentReadiness = waitForAgentReadiness;
|
|
4
|
+
const models_1 = require("./models.js");
|
|
5
|
+
/** All runtime-access responses remain private. Only fixed, credential-free copy escapes. */
|
|
6
|
+
async function waitForAgentReadiness(options) {
|
|
7
|
+
const budget = options.timeoutMs ?? 630000;
|
|
8
|
+
if (!Number.isFinite(budget) || budget < 1 || budget > 630000) {
|
|
9
|
+
throw new models_1.ToolingError('INVALID_REQUEST', 'Agent readiness timeout must be between 1 and 630000 milliseconds.');
|
|
10
|
+
}
|
|
11
|
+
let deadline = performance.now() + budget;
|
|
12
|
+
let lastProgress = '';
|
|
13
|
+
const progress = (message) => {
|
|
14
|
+
if (message !== lastProgress) {
|
|
15
|
+
lastProgress = message;
|
|
16
|
+
options.progress?.(message);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const timedOut = () => new models_1.ToolingError('PROCESS_TIMEOUT', 'The agent did not become ready in time. No message was sent. Try again.');
|
|
20
|
+
progress('Checking agent…');
|
|
21
|
+
while (true) {
|
|
22
|
+
options.signal?.throwIfAborted();
|
|
23
|
+
if (performance.now() >= deadline)
|
|
24
|
+
throw timedOut();
|
|
25
|
+
const access = await options.resolve(deadline);
|
|
26
|
+
options.signal?.throwIfAborted();
|
|
27
|
+
if (performance.now() >= deadline)
|
|
28
|
+
throw timedOut();
|
|
29
|
+
const interaction = access?.runtime_interaction;
|
|
30
|
+
if (!interaction || typeof interaction.can_submit !== 'boolean' || typeof interaction.state !== 'string') {
|
|
31
|
+
throw new models_1.ToolingError('API_ERROR', 'The platform did not provide a valid agent readiness decision. No message was sent.');
|
|
32
|
+
}
|
|
33
|
+
if (interaction.can_submit) {
|
|
34
|
+
if (!['ready', 'warning'].includes(interaction.state) || access.mode !== 'token'
|
|
35
|
+
|| typeof access.token !== 'string' || !access.token || typeof access.rpc_url !== 'string' || !access.rpc_url) {
|
|
36
|
+
throw new models_1.ToolingError('API_ERROR', 'The platform returned an incomplete agent readiness decision. No message was sent.');
|
|
37
|
+
}
|
|
38
|
+
progress('Agent ready');
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!['checking', 'starting', 'waking', 'updating'].includes(interaction.state)) {
|
|
42
|
+
const message = { update_required: 'The agent needs an update before it can accept messages.',
|
|
43
|
+
update_failed: 'The agent update failed.', unavailable: 'The agent is unavailable.' }[interaction.state]
|
|
44
|
+
?? 'The agent cannot accept messages right now.';
|
|
45
|
+
throw new models_1.ToolingError('API_ERROR', `${message} No message was sent. Try again when it is available.`);
|
|
46
|
+
}
|
|
47
|
+
const retry = interaction.retry_after_ms;
|
|
48
|
+
if (typeof retry !== 'number' || !Number.isFinite(retry) || retry < 0) {
|
|
49
|
+
throw new models_1.ToolingError('API_ERROR', 'The platform did not provide a valid readiness retry interval. No message was sent.');
|
|
50
|
+
}
|
|
51
|
+
const wakeDeadline = access.runtime_presence?.wake?.deadline_at;
|
|
52
|
+
if (typeof wakeDeadline === 'string' && Number.isFinite(Date.parse(wakeDeadline))) {
|
|
53
|
+
deadline = Math.min(deadline, performance.now() + Math.max(0, Date.parse(wakeDeadline) + 30000 - Date.now()));
|
|
54
|
+
}
|
|
55
|
+
progress(interaction.state === 'updating' ? 'Agent update in progress…'
|
|
56
|
+
: interaction.state === 'checking' ? 'Checking agent…' : 'Starting agent…');
|
|
57
|
+
const delay = Math.max(100, retry), remaining = deadline - performance.now();
|
|
58
|
+
if (remaining <= 0)
|
|
59
|
+
throw timedOut();
|
|
60
|
+
await new Promise((resolve, reject) => {
|
|
61
|
+
const finish = () => { options.signal?.removeEventListener('abort', abort); resolve(); };
|
|
62
|
+
const timer = setTimeout(finish, Math.min(delay, remaining));
|
|
63
|
+
const abort = () => { clearTimeout(timer); options.signal?.removeEventListener('abort', abort); reject(new models_1.ToolingError('CANCELLED', 'Agent preparation cancelled. No message was sent.')); };
|
|
64
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
65
|
+
if (options.signal?.aborted)
|
|
66
|
+
abort();
|
|
67
|
+
});
|
|
68
|
+
if (delay >= remaining)
|
|
69
|
+
throw timedOut();
|
|
70
|
+
}
|
|
71
|
+
}
|