@viewportai/daemon 0.20.1 → 0.22.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/README.md +34 -0
- package/dist/adapters/claude.d.ts +7 -1
- package/dist/adapters/claude.d.ts.map +1 -1
- package/dist/adapters/claude.js +71 -0
- package/dist/adapters/claude.js.map +1 -1
- package/dist/adapters/codex-event-normalizers.d.ts.map +1 -1
- package/dist/adapters/codex-event-normalizers.js +55 -2
- package/dist/adapters/codex-event-normalizers.js.map +1 -1
- package/dist/adapters/codex.d.ts +2 -1
- package/dist/adapters/codex.d.ts.map +1 -1
- package/dist/adapters/codex.js +35 -0
- package/dist/adapters/codex.js.map +1 -1
- package/dist/adapters/gemini-cli.d.ts +2 -1
- package/dist/adapters/gemini-cli.d.ts.map +1 -1
- package/dist/adapters/gemini-cli.js +24 -0
- package/dist/adapters/gemini-cli.js.map +1 -1
- package/dist/adapters/pty.d.ts +2 -1
- package/dist/adapters/pty.d.ts.map +1 -1
- package/dist/adapters/pty.js +26 -1
- package/dist/adapters/pty.js.map +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +7 -3
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands.d.ts +3 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +3 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/context-command.js +2 -0
- package/dist/cli/context-command.js.map +1 -1
- package/dist/cli/context-sync-target.d.ts +2 -0
- package/dist/cli/context-sync-target.d.ts.map +1 -1
- package/dist/cli/context-sync-target.js +4 -0
- package/dist/cli/context-sync-target.js.map +1 -1
- package/dist/cli/daemon-client.d.ts +1 -0
- package/dist/cli/daemon-client.d.ts.map +1 -1
- package/dist/cli/daemon-client.js +13 -1
- package/dist/cli/daemon-client.js.map +1 -1
- package/dist/cli/global-flags.d.ts.map +1 -1
- package/dist/cli/global-flags.js +3 -2
- package/dist/cli/global-flags.js.map +1 -1
- package/dist/cli/hook-command.d.ts +7 -0
- package/dist/cli/hook-command.d.ts.map +1 -1
- package/dist/cli/hook-command.js +35 -20
- package/dist/cli/hook-command.js.map +1 -1
- package/dist/cli/install-command.d.ts +5 -1
- package/dist/cli/install-command.d.ts.map +1 -1
- package/dist/cli/install-command.js +37 -28
- package/dist/cli/install-command.js.map +1 -1
- package/dist/cli/lifecycle-commands.d.ts.map +1 -1
- package/dist/cli/lifecycle-commands.js +5 -2
- package/dist/cli/lifecycle-commands.js.map +1 -1
- package/dist/cli/lifecycle-pair-command.d.ts.map +1 -1
- package/dist/cli/lifecycle-pair-command.js +51 -6
- package/dist/cli/lifecycle-pair-command.js.map +1 -1
- package/dist/cli/lifecycle-pair-server.d.ts +2 -0
- package/dist/cli/lifecycle-pair-server.d.ts.map +1 -1
- package/dist/cli/lifecycle-pair-server.js.map +1 -1
- package/dist/cli/lifecycle-status-command.d.ts.map +1 -1
- package/dist/cli/lifecycle-status-command.js +43 -0
- package/dist/cli/lifecycle-status-command.js.map +1 -1
- package/dist/cli/setup-command.d.ts.map +1 -1
- package/dist/cli/setup-command.js +8 -8
- package/dist/cli/setup-command.js.map +1 -1
- package/dist/cli/team-resource-command.d.ts +2 -0
- package/dist/cli/team-resource-command.d.ts.map +1 -0
- package/dist/cli/team-resource-command.js +364 -0
- package/dist/cli/team-resource-command.js.map +1 -0
- package/dist/cli/watch-command.d.ts +3 -0
- package/dist/cli/watch-command.d.ts.map +1 -0
- package/dist/cli/watch-command.js +42 -0
- package/dist/cli/watch-command.js.map +1 -0
- package/dist/cli/worker-command.d.ts +3 -0
- package/dist/cli/worker-command.d.ts.map +1 -0
- package/dist/cli/worker-command.js +132 -0
- package/dist/cli/worker-command.js.map +1 -0
- package/dist/cli/worker-profile.d.ts +65 -0
- package/dist/cli/worker-profile.d.ts.map +1 -0
- package/dist/cli/worker-profile.js +228 -0
- package/dist/cli/worker-profile.js.map +1 -0
- package/dist/cli/worker-runtime.d.ts +19 -0
- package/dist/cli/worker-runtime.d.ts.map +1 -0
- package/dist/cli/worker-runtime.js +606 -0
- package/dist/cli/worker-runtime.js.map +1 -0
- package/dist/cli/workflow-managed-worker-format.d.ts +4 -3
- package/dist/cli/workflow-managed-worker-format.d.ts.map +1 -1
- package/dist/cli/workflow-managed-worker-format.js +71 -10
- package/dist/cli/workflow-managed-worker-format.js.map +1 -1
- package/dist/cli/workflow-managed-worker-types.d.ts +17 -0
- package/dist/cli/workflow-managed-worker-types.d.ts.map +1 -1
- package/dist/cli/workflow-managed-worker.d.ts.map +1 -1
- package/dist/cli/workflow-managed-worker.js +568 -39
- package/dist/cli/workflow-managed-worker.js.map +1 -1
- package/dist/config-resolution/provider-defaults.d.ts.map +1 -1
- package/dist/config-resolution/provider-defaults.js +4 -0
- package/dist/config-resolution/provider-defaults.js.map +1 -1
- package/dist/config-resolution/schema.d.ts +2 -0
- package/dist/config-resolution/schema.d.ts.map +1 -1
- package/dist/config-resolution/schema.js +2 -0
- package/dist/config-resolution/schema.js.map +1 -1
- package/dist/config-resolution/types.d.ts +1 -1
- package/dist/config-resolution/types.d.ts.map +1 -1
- package/dist/context/local-edge-sync.d.ts +2 -0
- package/dist/context/local-edge-sync.d.ts.map +1 -1
- package/dist/context/local-edge-sync.js +2 -0
- package/dist/context/local-edge-sync.js.map +1 -1
- package/dist/context-providers/confluence-provider.d.ts +3 -0
- package/dist/context-providers/confluence-provider.d.ts.map +1 -0
- package/dist/context-providers/confluence-provider.js +170 -0
- package/dist/context-providers/confluence-provider.js.map +1 -0
- package/dist/context-providers/notion-provider.d.ts +3 -0
- package/dist/context-providers/notion-provider.d.ts.map +1 -0
- package/dist/context-providers/notion-provider.js +157 -0
- package/dist/context-providers/notion-provider.js.map +1 -0
- package/dist/context-providers/registry.d.ts.map +1 -1
- package/dist/context-providers/registry.js +9 -1
- package/dist/context-providers/registry.js.map +1 -1
- package/dist/context-providers/types.d.ts +18 -0
- package/dist/context-providers/types.d.ts.map +1 -1
- package/dist/core/config-schema.d.ts +96 -3
- package/dist/core/config-schema.d.ts.map +1 -1
- package/dist/core/config-schema.js +53 -0
- package/dist/core/config-schema.js.map +1 -1
- package/dist/core/config.d.ts +88 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +6 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/daemon.d.ts +5 -1
- package/dist/core/daemon.d.ts.map +1 -1
- package/dist/core/daemon.js +8 -0
- package/dist/core/daemon.js.map +1 -1
- package/dist/core/interfaces.d.ts +137 -1
- package/dist/core/interfaces.d.ts.map +1 -1
- package/dist/core/permission-coordinator.d.ts.map +1 -1
- package/dist/core/permission-coordinator.js +4 -2
- package/dist/core/permission-coordinator.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +47 -20
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/types.d.ts +31 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/discovery/watcher.d.ts.map +1 -1
- package/dist/discovery/watcher.js +51 -8
- package/dist/discovery/watcher.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/loader.d.ts.map +1 -1
- package/dist/plugins/loader.js +10 -1
- package/dist/plugins/loader.js.map +1 -1
- package/dist/security/child-env.d.ts +3 -0
- package/dist/security/child-env.d.ts.map +1 -0
- package/dist/security/child-env.js +44 -0
- package/dist/security/child-env.js.map +1 -0
- package/dist/server/http-request-schemas.d.ts +6 -0
- package/dist/server/http-request-schemas.d.ts.map +1 -1
- package/dist/server/http-request-schemas.js +15 -0
- package/dist/server/http-request-schemas.js.map +1 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +4 -2
- package/dist/server/http-server.js.map +1 -1
- package/dist/workflows/action-adapters.d.ts.map +1 -1
- package/dist/workflows/action-adapters.js +42 -0
- package/dist/workflows/action-adapters.js.map +1 -1
- package/dist/workflows/action-provider-adapters.d.ts.map +1 -1
- package/dist/workflows/action-provider-adapters.js +156 -13
- package/dist/workflows/action-provider-adapters.js.map +1 -1
- package/dist/workflows/action-provider-utils.d.ts +6 -0
- package/dist/workflows/action-provider-utils.d.ts.map +1 -1
- package/dist/workflows/action-provider-utils.js +21 -0
- package/dist/workflows/action-provider-utils.js.map +1 -1
- package/dist/workflows/approval-actor.d.ts +6 -0
- package/dist/workflows/approval-actor.d.ts.map +1 -0
- package/dist/workflows/approval-actor.js +27 -0
- package/dist/workflows/approval-actor.js.map +1 -0
- package/dist/workflows/approval-on-reject.js +1 -0
- package/dist/workflows/approval-on-reject.js.map +1 -1
- package/dist/workflows/checkout-node.d.ts +21 -0
- package/dist/workflows/checkout-node.d.ts.map +1 -0
- package/dist/workflows/checkout-node.js +181 -0
- package/dist/workflows/checkout-node.js.map +1 -0
- package/dist/workflows/context-node-resolver.d.ts +48 -1
- package/dist/workflows/context-node-resolver.d.ts.map +1 -1
- package/dist/workflows/context-node-resolver.js +405 -8
- package/dist/workflows/context-node-resolver.js.map +1 -1
- package/dist/workflows/context-update-targets.d.ts +12 -0
- package/dist/workflows/context-update-targets.d.ts.map +1 -0
- package/dist/workflows/context-update-targets.js +52 -0
- package/dist/workflows/context-update-targets.js.map +1 -0
- package/dist/workflows/daemon-session.d.ts +11 -0
- package/dist/workflows/daemon-session.d.ts.map +1 -1
- package/dist/workflows/daemon-session.js +168 -6
- package/dist/workflows/daemon-session.js.map +1 -1
- package/dist/workflows/event-types.d.ts +1 -1
- package/dist/workflows/event-types.d.ts.map +1 -1
- package/dist/workflows/expression.d.ts +4 -0
- package/dist/workflows/expression.d.ts.map +1 -1
- package/dist/workflows/expression.js +1 -1
- package/dist/workflows/expression.js.map +1 -1
- package/dist/workflows/git-publish-node.d.ts +23 -0
- package/dist/workflows/git-publish-node.d.ts.map +1 -0
- package/dist/workflows/git-publish-node.js +237 -0
- package/dist/workflows/git-publish-node.js.map +1 -0
- package/dist/workflows/inline-agent-types.d.ts +7 -0
- package/dist/workflows/inline-agent-types.d.ts.map +1 -1
- package/dist/workflows/inline-agents.d.ts +4 -1
- package/dist/workflows/inline-agents.d.ts.map +1 -1
- package/dist/workflows/inline-agents.js +24 -1
- package/dist/workflows/inline-agents.js.map +1 -1
- package/dist/workflows/loop-executor.js +1 -0
- package/dist/workflows/loop-executor.js.map +1 -1
- package/dist/workflows/node-executor.d.ts +7 -0
- package/dist/workflows/node-executor.d.ts.map +1 -1
- package/dist/workflows/node-executor.js +135 -7
- package/dist/workflows/node-executor.js.map +1 -1
- package/dist/workflows/node-registry.d.ts.map +1 -1
- package/dist/workflows/node-registry.js +553 -4
- package/dist/workflows/node-registry.js.map +1 -1
- package/dist/workflows/parser.d.ts.map +1 -1
- package/dist/workflows/parser.js +70 -5
- package/dist/workflows/parser.js.map +1 -1
- package/dist/workflows/platform-command-applier.d.ts.map +1 -1
- package/dist/workflows/platform-command-applier.js +5 -14
- package/dist/workflows/platform-command-applier.js.map +1 -1
- package/dist/workflows/platform-context-client.d.ts +74 -0
- package/dist/workflows/platform-context-client.d.ts.map +1 -0
- package/dist/workflows/platform-context-client.js +228 -0
- package/dist/workflows/platform-context-client.js.map +1 -0
- package/dist/workflows/platform-sync-format.d.ts +1 -1
- package/dist/workflows/platform-sync-format.d.ts.map +1 -1
- package/dist/workflows/platform-sync-format.js +3 -1
- package/dist/workflows/platform-sync-format.js.map +1 -1
- package/dist/workflows/platform-sync-payload.d.ts +1 -0
- package/dist/workflows/platform-sync-payload.d.ts.map +1 -1
- package/dist/workflows/platform-sync-payload.js +160 -8
- package/dist/workflows/platform-sync-payload.js.map +1 -1
- package/dist/workflows/platform-sync.d.ts.map +1 -1
- package/dist/workflows/platform-sync.js +10 -1
- package/dist/workflows/platform-sync.js.map +1 -1
- package/dist/workflows/plugin-loader.d.ts.map +1 -1
- package/dist/workflows/plugin-loader.js +3 -0
- package/dist/workflows/plugin-loader.js.map +1 -1
- package/dist/workflows/preflight.d.ts.map +1 -1
- package/dist/workflows/preflight.js +9 -1
- package/dist/workflows/preflight.js.map +1 -1
- package/dist/workflows/prompt-output.d.ts +6 -2
- package/dist/workflows/prompt-output.d.ts.map +1 -1
- package/dist/workflows/prompt-output.js +8 -2
- package/dist/workflows/prompt-output.js.map +1 -1
- package/dist/workflows/run-preparation.d.ts +24 -0
- package/dist/workflows/run-preparation.d.ts.map +1 -0
- package/dist/workflows/run-preparation.js +258 -0
- package/dist/workflows/run-preparation.js.map +1 -0
- package/dist/workflows/run-types.d.ts +11 -0
- package/dist/workflows/run-types.d.ts.map +1 -1
- package/dist/workflows/runner-scheduler.d.ts +2 -0
- package/dist/workflows/runner-scheduler.d.ts.map +1 -1
- package/dist/workflows/runner-scheduler.js +35 -4
- package/dist/workflows/runner-scheduler.js.map +1 -1
- package/dist/workflows/runner-shared.d.ts.map +1 -1
- package/dist/workflows/runner-shared.js +9 -0
- package/dist/workflows/runner-shared.js.map +1 -1
- package/dist/workflows/runner.d.ts +2 -0
- package/dist/workflows/runner.d.ts.map +1 -1
- package/dist/workflows/runner.js +171 -6
- package/dist/workflows/runner.js.map +1 -1
- package/dist/workflows/runtime-helpers.d.ts +20 -1
- package/dist/workflows/runtime-helpers.d.ts.map +1 -1
- package/dist/workflows/runtime-helpers.js +98 -3
- package/dist/workflows/runtime-helpers.js.map +1 -1
- package/dist/workflows/session-output.d.ts +9 -0
- package/dist/workflows/session-output.d.ts.map +1 -1
- package/dist/workflows/session-output.js +156 -0
- package/dist/workflows/session-output.js.map +1 -1
- package/dist/workflows/session-policy.d.ts +30 -0
- package/dist/workflows/session-policy.d.ts.map +1 -0
- package/dist/workflows/session-policy.js +45 -0
- package/dist/workflows/session-policy.js.map +1 -0
- package/dist/workflows/structured-outputs.d.ts +5 -0
- package/dist/workflows/structured-outputs.d.ts.map +1 -1
- package/dist/workflows/structured-outputs.js +82 -3
- package/dist/workflows/structured-outputs.js.map +1 -1
- package/dist/workflows/subflow-executor.js +5 -1
- package/dist/workflows/subflow-executor.js.map +1 -1
- package/dist/workflows/types.d.ts +107 -4
- package/dist/workflows/types.d.ts.map +1 -1
- package/dist/workflows/workflow-authority-contract.d.ts +25 -0
- package/dist/workflows/workflow-authority-contract.d.ts.map +1 -0
- package/dist/workflows/workflow-authority-contract.js +366 -0
- package/dist/workflows/workflow-authority-contract.js.map +1 -0
- package/dist/workflows/workflow-executor-schema.d.ts +1 -1
- package/dist/workflows/workflow-production-schema.d.ts +62 -10
- package/dist/workflows/workflow-production-schema.d.ts.map +1 -1
- package/dist/workflows/workflow-production-schema.js +63 -4
- package/dist/workflows/workflow-production-schema.js.map +1 -1
- package/dist/workflows/workflow-production-types.d.ts +42 -3
- package/dist/workflows/workflow-production-types.d.ts.map +1 -1
- package/dist/workflows/workflow-schema-common.d.ts +248 -2
- package/dist/workflows/workflow-schema-common.d.ts.map +1 -1
- package/dist/workflows/workflow-schema-common.js +58 -2
- package/dist/workflows/workflow-schema-common.js.map +1 -1
- package/dist/workflows/workflow-schema.d.ts +1966 -16
- package/dist/workflows/workflow-schema.d.ts.map +1 -1
- package/dist/workflows/workflow-schema.js +97 -3
- package/dist/workflows/workflow-schema.js.map +1 -1
- package/docs/releasing.md +4 -1
- package/node_modules/@viewportai/context-engine/schemas/context_event_v1.schema.json +2 -0
- package/node_modules/@viewportai/context-engine/src/repo/candidates.js +2 -0
- package/node_modules/@viewportai/context-engine/src/repo/events.js +15 -1
- package/node_modules/@viewportai/context-engine/src/repo/vault.js +18 -2
- package/package.json +2 -2
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import path from 'node:path';
|
|
1
3
|
import { executeLoopNode } from './loop-executor.js';
|
|
2
4
|
import { executeContextNode } from './context-node-resolver.js';
|
|
3
5
|
import { executeActionAdapter, WorkflowActionError } from './action-adapters.js';
|
|
4
|
-
import { addEvent, renderOptionalTemplate, renderTemplate, resolveNodeCwd, runShellNode, } from './runtime-helpers.js';
|
|
6
|
+
import { addEvent, renderOptionalTemplate, runArgvNode, renderShellCommandTemplate, renderTemplate, resolveNodeCwd, runShellNode, ShellNodeError, } from './runtime-helpers.js';
|
|
7
|
+
import { envNameForCredentialRef } from './action-provider-utils.js';
|
|
5
8
|
import { executeSubflowNode } from './subflow-executor.js';
|
|
6
9
|
import { buildExpressionContext, evaluateConditionExpression } from './expression.js';
|
|
7
10
|
import { sanitizePlanProposalMetadata } from '../hooks/plan-extractor.js';
|
|
11
|
+
import { readPromptNodeOutput } from './prompt-output.js';
|
|
12
|
+
import { shellAuthorityDenial } from './workflow-authority-contract.js';
|
|
13
|
+
import { checkoutAuthorityDenial, executeCheckoutNode } from './checkout-node.js';
|
|
14
|
+
import { executeGitPublishNode, gitPublishAuthorityDenial } from './git-publish-node.js';
|
|
8
15
|
/**
|
|
9
16
|
* Mutable registry of node executors keyed by `node.type`. Built-ins are
|
|
10
17
|
* registered at module load (immediately below); the plugin loader extends
|
|
@@ -13,20 +20,224 @@ import { sanitizePlanProposalMetadata } from '../hooks/plan-extractor.js';
|
|
|
13
20
|
* is one entry, not five files.
|
|
14
21
|
*/
|
|
15
22
|
export const NODE_EXECUTORS = new Map();
|
|
23
|
+
const SECRET_LIKE_ENV_NAME_PATTERN = /(^|_)(AUTHORIZATION|CREDENTIAL|PASSWORD|PASSWD|PRIVATE_KEY|SECRET|TOKEN|API_KEY)(_|$)/i;
|
|
24
|
+
function isSecretLikeEnvName(name) {
|
|
25
|
+
return SECRET_LIKE_ENV_NAME_PATTERN.test(name);
|
|
26
|
+
}
|
|
16
27
|
export function registerNodeExecutor(type, executor) {
|
|
17
28
|
NODE_EXECUTORS.set(type, executor);
|
|
18
29
|
}
|
|
19
30
|
const BUILTIN_NODE_EXECUTORS = {
|
|
31
|
+
checkout: async (context, run, nodeId, node) => {
|
|
32
|
+
if (node.type !== 'checkout')
|
|
33
|
+
return { result: 'completed' };
|
|
34
|
+
const state = run.nodes[nodeId];
|
|
35
|
+
const renderedNode = {
|
|
36
|
+
...node,
|
|
37
|
+
repository: await renderTemplate(node.repository, run),
|
|
38
|
+
...(node.remote ? { remote: await renderTemplate(node.remote, run) } : {}),
|
|
39
|
+
...(node.path ? { path: await renderTemplate(node.path, run) } : {}),
|
|
40
|
+
...(node.ref ? { ref: await renderTemplate(node.ref, run) } : {}),
|
|
41
|
+
...(node.branch ? { branch: await renderTemplate(node.branch, run) } : {}),
|
|
42
|
+
};
|
|
43
|
+
const denial = checkoutAuthorityDenial(run, nodeId, renderedNode);
|
|
44
|
+
if (denial) {
|
|
45
|
+
addEvent(run, 'checkout-blocked', denial.detail, { workflow_authority_denial: denial }, nodeId);
|
|
46
|
+
throw new Error(denial.detail);
|
|
47
|
+
}
|
|
48
|
+
const credentialRef = node.credentialRef;
|
|
49
|
+
const credential = credentialRef && credentialRef.trim() !== ''
|
|
50
|
+
? {
|
|
51
|
+
envName: envNameForCredentialRef(credentialRef),
|
|
52
|
+
secret: context.runtimeSecretEnv?.[envNameForCredentialRef(credentialRef)] ??
|
|
53
|
+
process.env[envNameForCredentialRef(credentialRef)],
|
|
54
|
+
}
|
|
55
|
+
: undefined;
|
|
56
|
+
const result = await executeCheckoutNode(run, renderedNode, credential);
|
|
57
|
+
if (state) {
|
|
58
|
+
state.output = JSON.stringify(result);
|
|
59
|
+
state.worktreePath = result.path;
|
|
60
|
+
state.outputs = {
|
|
61
|
+
...(state.outputs ?? {}),
|
|
62
|
+
repository: result.repository,
|
|
63
|
+
path: result.path,
|
|
64
|
+
ref: result.ref,
|
|
65
|
+
branch: result.branch,
|
|
66
|
+
commit: result.commit,
|
|
67
|
+
sourceCategory: result.sourceCategory,
|
|
68
|
+
readWriteMode: result.readWriteMode,
|
|
69
|
+
};
|
|
70
|
+
state.metadata = {
|
|
71
|
+
...(state.metadata ?? {}),
|
|
72
|
+
checkout: {
|
|
73
|
+
schema: 'viewport.checkout_receipt/v1',
|
|
74
|
+
repository: result.repository,
|
|
75
|
+
remote: result.remote,
|
|
76
|
+
path: result.path,
|
|
77
|
+
source_category: result.sourceCategory,
|
|
78
|
+
read_write_mode: result.readWriteMode,
|
|
79
|
+
requested_ref: result.ref,
|
|
80
|
+
requested_branch: result.branch,
|
|
81
|
+
ref: result.ref,
|
|
82
|
+
branch: result.branch,
|
|
83
|
+
exact_commit: result.commit,
|
|
84
|
+
commit: result.commit,
|
|
85
|
+
credentialMode: result.credentialMode,
|
|
86
|
+
credentialRef: result.credentialRef,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
addEvent(run, 'checkout-completed', `Checked out ${result.repository}`, {
|
|
91
|
+
repository: result.repository,
|
|
92
|
+
remote: result.remote,
|
|
93
|
+
path: result.path,
|
|
94
|
+
source_category: result.sourceCategory,
|
|
95
|
+
read_write_mode: result.readWriteMode,
|
|
96
|
+
ref: result.ref,
|
|
97
|
+
branch: result.branch,
|
|
98
|
+
exact_commit: result.commit,
|
|
99
|
+
commit: result.commit,
|
|
100
|
+
}, nodeId);
|
|
101
|
+
return { result: 'completed', artifactCwd: result.path };
|
|
102
|
+
},
|
|
103
|
+
git_publish: async (context, run, nodeId, node) => {
|
|
104
|
+
if (node.type !== 'git_publish')
|
|
105
|
+
return { result: 'completed' };
|
|
106
|
+
const state = run.nodes[nodeId];
|
|
107
|
+
const renderedNode = {
|
|
108
|
+
...node,
|
|
109
|
+
repository: await renderTemplate(node.repository, run),
|
|
110
|
+
...(node.credentialRef
|
|
111
|
+
? { credentialRef: await renderTemplate(node.credentialRef, run) }
|
|
112
|
+
: {}),
|
|
113
|
+
...(node.paths
|
|
114
|
+
? { paths: await Promise.all(node.paths.map((entry) => renderTemplate(entry, run))) }
|
|
115
|
+
: {}),
|
|
116
|
+
};
|
|
117
|
+
const input = {
|
|
118
|
+
cwd: resolveNodeCwd(run.directoryPath, await renderOptionalTemplate(node.cwd, run)),
|
|
119
|
+
branch: await renderTemplate(node.branch, run),
|
|
120
|
+
message: await renderTemplate(node.message, run),
|
|
121
|
+
};
|
|
122
|
+
const denial = await gitPublishAuthorityDenial(run, nodeId, renderedNode, input);
|
|
123
|
+
if (denial) {
|
|
124
|
+
addEvent(run, 'git-publish-blocked', denial.detail, { workflow_authority_denial: denial }, nodeId);
|
|
125
|
+
throw new Error(denial.detail);
|
|
126
|
+
}
|
|
127
|
+
const credentialRef = renderedNode.credentialRef;
|
|
128
|
+
const credential = credentialRef && credentialRef.trim() !== ''
|
|
129
|
+
? {
|
|
130
|
+
envName: envNameForCredentialRef(credentialRef),
|
|
131
|
+
secret: context.runtimeSecretEnv?.[envNameForCredentialRef(credentialRef)] ??
|
|
132
|
+
process.env[envNameForCredentialRef(credentialRef)],
|
|
133
|
+
}
|
|
134
|
+
: undefined;
|
|
135
|
+
const result = await executeGitPublishNode(renderedNode, input, credential);
|
|
136
|
+
if (state) {
|
|
137
|
+
state.output = JSON.stringify(result);
|
|
138
|
+
state.outputs = {
|
|
139
|
+
...(state.outputs ?? {}),
|
|
140
|
+
repository: result.repository,
|
|
141
|
+
branch: result.branch,
|
|
142
|
+
commit: result.commit,
|
|
143
|
+
pushed: result.pushed,
|
|
144
|
+
changed: result.changed,
|
|
145
|
+
};
|
|
146
|
+
state.metadata = {
|
|
147
|
+
...(state.metadata ?? {}),
|
|
148
|
+
git_publish: {
|
|
149
|
+
schema: 'viewport.git_publish_receipt/v1',
|
|
150
|
+
repository: result.repository,
|
|
151
|
+
branch: result.branch,
|
|
152
|
+
commit: result.commit,
|
|
153
|
+
pushed: result.pushed,
|
|
154
|
+
changed: result.changed,
|
|
155
|
+
credentialMode: result.credentialMode,
|
|
156
|
+
credentialRef: result.credentialRef,
|
|
157
|
+
},
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
addEvent(run, 'git-publish-completed', `Published ${result.repository} branch ${result.branch}`, { ...result }, nodeId);
|
|
161
|
+
return { result: 'completed', artifactCwd: input.cwd };
|
|
162
|
+
},
|
|
20
163
|
shell: async (context, run, nodeId, node) => {
|
|
21
164
|
if (node.type !== 'shell')
|
|
22
165
|
return { result: 'completed' };
|
|
23
166
|
const state = run.nodes[nodeId];
|
|
167
|
+
const startedAt = Date.now();
|
|
24
168
|
const artifactCwd = resolveNodeCwd(run.directoryPath, await renderOptionalTemplate(node.cwd, run));
|
|
169
|
+
const invocation = await renderShellInvocation(node, run);
|
|
170
|
+
const env = await resolveNodeEnv(context, run, node);
|
|
171
|
+
if (state) {
|
|
172
|
+
state.metadata = {
|
|
173
|
+
...(state.metadata ?? {}),
|
|
174
|
+
shell_execution: buildShellExecutionReceipt({
|
|
175
|
+
run,
|
|
176
|
+
nodeId,
|
|
177
|
+
commandDigestMaterial: invocation.digestMaterial,
|
|
178
|
+
executor: invocation.executor,
|
|
179
|
+
cwd: artifactCwd,
|
|
180
|
+
env,
|
|
181
|
+
timeoutSeconds: node.timeoutSeconds,
|
|
182
|
+
startedAt,
|
|
183
|
+
status: 'preflight',
|
|
184
|
+
}),
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const workspaceDenial = shellWorkspaceCwdDenial(run, nodeId, artifactCwd);
|
|
188
|
+
if (workspaceDenial) {
|
|
189
|
+
if (state) {
|
|
190
|
+
state.metadata = {
|
|
191
|
+
...(state.metadata ?? {}),
|
|
192
|
+
shell_execution: buildShellExecutionReceipt({
|
|
193
|
+
run,
|
|
194
|
+
nodeId,
|
|
195
|
+
commandDigestMaterial: invocation.digestMaterial,
|
|
196
|
+
executor: invocation.executor,
|
|
197
|
+
cwd: artifactCwd,
|
|
198
|
+
env,
|
|
199
|
+
timeoutSeconds: node.timeoutSeconds,
|
|
200
|
+
startedAt,
|
|
201
|
+
completedAt: Date.now(),
|
|
202
|
+
status: 'denied',
|
|
203
|
+
denial: workspaceDenial,
|
|
204
|
+
}),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
addEvent(run, 'shell-blocked', workspaceDenial.detail, { workflow_authority_denial: workspaceDenial }, nodeId);
|
|
208
|
+
throw new Error(workspaceDenial.detail);
|
|
209
|
+
}
|
|
210
|
+
const denial = shellAuthorityDenial(run, nodeId, invocation.authorityCommand, artifactCwd, {
|
|
211
|
+
executorKind: typeof invocation.executor.kind === 'string' ? invocation.executor.kind : undefined,
|
|
212
|
+
});
|
|
213
|
+
if (denial) {
|
|
214
|
+
if (state) {
|
|
215
|
+
state.metadata = {
|
|
216
|
+
...(state.metadata ?? {}),
|
|
217
|
+
shell_execution: buildShellExecutionReceipt({
|
|
218
|
+
run,
|
|
219
|
+
nodeId,
|
|
220
|
+
commandDigestMaterial: invocation.digestMaterial,
|
|
221
|
+
executor: invocation.executor,
|
|
222
|
+
cwd: artifactCwd,
|
|
223
|
+
env,
|
|
224
|
+
timeoutSeconds: node.timeoutSeconds,
|
|
225
|
+
startedAt,
|
|
226
|
+
completedAt: Date.now(),
|
|
227
|
+
status: 'denied',
|
|
228
|
+
denial,
|
|
229
|
+
}),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
addEvent(run, 'shell-blocked', denial.detail, { workflow_authority_denial: denial }, nodeId);
|
|
233
|
+
throw new Error(denial.detail);
|
|
234
|
+
}
|
|
25
235
|
const abort = context.shellAbortRegistry.create(run.id, `node:${nodeId}`);
|
|
26
236
|
let result;
|
|
27
237
|
try {
|
|
28
|
-
result = await
|
|
238
|
+
result = await invocation.run({
|
|
29
239
|
cwd: artifactCwd,
|
|
240
|
+
env,
|
|
30
241
|
timeoutSeconds: node.timeoutSeconds,
|
|
31
242
|
signal: abort.signal,
|
|
32
243
|
onOutput: ({ source, chunk, output }) => {
|
|
@@ -36,12 +247,55 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
36
247
|
},
|
|
37
248
|
});
|
|
38
249
|
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
if (state) {
|
|
252
|
+
if (error instanceof ShellNodeError) {
|
|
253
|
+
state.output = error.output || state.output;
|
|
254
|
+
state.exitCode = error.exitCode ?? undefined;
|
|
255
|
+
}
|
|
256
|
+
state.metadata = {
|
|
257
|
+
...(state.metadata ?? {}),
|
|
258
|
+
shell_execution: buildShellExecutionReceipt({
|
|
259
|
+
run,
|
|
260
|
+
nodeId,
|
|
261
|
+
commandDigestMaterial: invocation.digestMaterial,
|
|
262
|
+
executor: invocation.executor,
|
|
263
|
+
cwd: artifactCwd,
|
|
264
|
+
env,
|
|
265
|
+
timeoutSeconds: node.timeoutSeconds,
|
|
266
|
+
startedAt,
|
|
267
|
+
completedAt: Date.now(),
|
|
268
|
+
status: error instanceof ShellNodeError && error.message.includes('canceled')
|
|
269
|
+
? 'canceled'
|
|
270
|
+
: 'failed',
|
|
271
|
+
exitCode: error instanceof ShellNodeError ? error.exitCode : null,
|
|
272
|
+
}),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
39
277
|
finally {
|
|
40
278
|
abort.dispose();
|
|
41
279
|
}
|
|
42
280
|
if (state) {
|
|
43
281
|
state.output = result.output;
|
|
44
282
|
state.exitCode = result.exitCode;
|
|
283
|
+
state.metadata = {
|
|
284
|
+
...(state.metadata ?? {}),
|
|
285
|
+
shell_execution: buildShellExecutionReceipt({
|
|
286
|
+
run,
|
|
287
|
+
nodeId,
|
|
288
|
+
commandDigestMaterial: invocation.digestMaterial,
|
|
289
|
+
executor: invocation.executor,
|
|
290
|
+
cwd: artifactCwd,
|
|
291
|
+
env,
|
|
292
|
+
timeoutSeconds: node.timeoutSeconds,
|
|
293
|
+
startedAt,
|
|
294
|
+
completedAt: Date.now(),
|
|
295
|
+
status: 'completed',
|
|
296
|
+
exitCode: result.exitCode,
|
|
297
|
+
}),
|
|
298
|
+
};
|
|
45
299
|
}
|
|
46
300
|
addEvent(run, 'node-output', `Node ${nodeId} produced shell output`, { output: result.output, exitCode: result.exitCode }, nodeId);
|
|
47
301
|
return { result: 'completed', artifactCwd };
|
|
@@ -50,7 +304,8 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
50
304
|
if (node.type !== 'prompt')
|
|
51
305
|
return { result: 'completed' };
|
|
52
306
|
await helpers.executePromptNode(context, run, nodeId, node);
|
|
53
|
-
|
|
307
|
+
const artifactCwd = run.nodes[nodeId]?.worktreePath;
|
|
308
|
+
return artifactCwd ? { result: 'completed', artifactCwd } : { result: 'completed' };
|
|
54
309
|
},
|
|
55
310
|
agent: async (context, run, nodeId, node, helpers) => {
|
|
56
311
|
if (node.type !== 'agent')
|
|
@@ -63,7 +318,8 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
63
318
|
if (node.handoff) {
|
|
64
319
|
addEvent(run, 'node-output', `Agent node ${nodeId} prepared handoff metadata`, { handoff: node.handoff }, nodeId);
|
|
65
320
|
}
|
|
66
|
-
|
|
321
|
+
const artifactCwd = run.nodes[nodeId]?.worktreePath;
|
|
322
|
+
return artifactCwd ? { result: 'completed', artifactCwd } : { result: 'completed' };
|
|
67
323
|
},
|
|
68
324
|
approval: async (context, run, nodeId, node, helpers) => {
|
|
69
325
|
if (node.type !== 'approval')
|
|
@@ -83,6 +339,86 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
83
339
|
await executeContextNode(run, nodeId, node);
|
|
84
340
|
return { result: 'completed' };
|
|
85
341
|
},
|
|
342
|
+
context_update: async (context, run, nodeId, node) => {
|
|
343
|
+
if (node.type !== 'context_update')
|
|
344
|
+
return { result: 'completed' };
|
|
345
|
+
const state = run.nodes[nodeId];
|
|
346
|
+
const targetRef = await renderTemplate(node.targetRef, run);
|
|
347
|
+
const title = await renderTemplate(node.title, run);
|
|
348
|
+
const summary = await renderOptionalTemplate(node.summary, run);
|
|
349
|
+
const idempotencyKey = await renderOptionalTemplate(node.idempotencyKey, run);
|
|
350
|
+
const patch = await renderContextUpdatePatch(node.patch, run);
|
|
351
|
+
try {
|
|
352
|
+
const proposal = await context.platformContextClient?.proposeContextUpdate({
|
|
353
|
+
run,
|
|
354
|
+
nodeId,
|
|
355
|
+
targetRef,
|
|
356
|
+
title,
|
|
357
|
+
summary,
|
|
358
|
+
patch,
|
|
359
|
+
idempotencyKey,
|
|
360
|
+
});
|
|
361
|
+
const output = proposal?.proposalId
|
|
362
|
+
? `Context update proposal ${proposal.proposalId}`
|
|
363
|
+
: `Context update proposal prepared for ${targetRef}`;
|
|
364
|
+
if (state) {
|
|
365
|
+
state.output = output;
|
|
366
|
+
state.outputs = {
|
|
367
|
+
...(state.outputs ?? {}),
|
|
368
|
+
target_ref: targetRef,
|
|
369
|
+
proposal_id: proposal?.proposalId ?? null,
|
|
370
|
+
inbox_item_id: proposal?.inboxItemId ?? null,
|
|
371
|
+
status: proposal?.status ?? 'prepared',
|
|
372
|
+
};
|
|
373
|
+
state.metadata = {
|
|
374
|
+
...(state.metadata ?? {}),
|
|
375
|
+
context_update: {
|
|
376
|
+
target_ref: targetRef,
|
|
377
|
+
title,
|
|
378
|
+
summary: summary ?? null,
|
|
379
|
+
patch: redactedContextUpdatePatch(patch),
|
|
380
|
+
patch_digest: patchDigest(patch),
|
|
381
|
+
idempotency_key: idempotencyKey ?? null,
|
|
382
|
+
proposal_id: proposal?.proposalId ?? null,
|
|
383
|
+
inbox_item_id: proposal?.inboxItemId ?? null,
|
|
384
|
+
status: proposal?.status ?? 'prepared',
|
|
385
|
+
plaintext_patch_persisted: false,
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
addEvent(run, 'context-update-proposed', proposal?.proposalId
|
|
390
|
+
? `Context update proposal ${proposal.proposalId} created for ${targetRef}`
|
|
391
|
+
: `Context update proposal prepared for ${targetRef}`, {
|
|
392
|
+
targetRef,
|
|
393
|
+
title,
|
|
394
|
+
proposalId: proposal?.proposalId ?? null,
|
|
395
|
+
inboxItemId: proposal?.inboxItemId ?? null,
|
|
396
|
+
status: proposal?.status ?? 'prepared',
|
|
397
|
+
patchDigest: patchDigest(patch),
|
|
398
|
+
}, nodeId);
|
|
399
|
+
return { result: 'completed' };
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
403
|
+
if (state) {
|
|
404
|
+
state.output = `Context update proposal failed: ${message}`;
|
|
405
|
+
state.metadata = {
|
|
406
|
+
...(state.metadata ?? {}),
|
|
407
|
+
context_update: {
|
|
408
|
+
target_ref: targetRef,
|
|
409
|
+
title,
|
|
410
|
+
status: 'failed',
|
|
411
|
+
error: message,
|
|
412
|
+
patch: redactedContextUpdatePatch(patch),
|
|
413
|
+
patch_digest: patchDigest(patch),
|
|
414
|
+
plaintext_patch_persisted: false,
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
addEvent(run, 'context-update-proposal-failed', `Context update proposal failed for ${targetRef}: ${message}`, { targetRef, title, error: message, patchDigest: patchDigest(patch) }, nodeId);
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
},
|
|
86
422
|
condition: async (_context, run, nodeId, node) => {
|
|
87
423
|
if (node.type !== 'condition')
|
|
88
424
|
return { result: 'completed' };
|
|
@@ -191,6 +527,7 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
191
527
|
if (node.type !== 'plan')
|
|
192
528
|
return { result: 'completed' };
|
|
193
529
|
const state = run.nodes[nodeId];
|
|
530
|
+
await backfillPromptDependencyOutputs(run, node.needs);
|
|
194
531
|
const title = await renderTemplate(node.title ?? nodeId, run);
|
|
195
532
|
const body = await renderTemplate(node.body, run);
|
|
196
533
|
const summary = await renderOptionalTemplate(node.summary, run);
|
|
@@ -205,6 +542,8 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
205
542
|
body,
|
|
206
543
|
source: node.source ?? 'workflow',
|
|
207
544
|
sourceRef: sourceRef || `viewport://workflow-runs/${run.id}/nodes/${nodeId}`,
|
|
545
|
+
...(node.recipients ? { recipients: node.recipients } : {}),
|
|
546
|
+
...(node.revision ? { revision: node.revision } : {}),
|
|
208
547
|
},
|
|
209
548
|
};
|
|
210
549
|
}
|
|
@@ -231,8 +570,218 @@ const BUILTIN_NODE_EXECUTORS = {
|
|
|
231
570
|
return { result: 'blocked' };
|
|
232
571
|
},
|
|
233
572
|
};
|
|
573
|
+
async function backfillPromptDependencyOutputs(run, needs) {
|
|
574
|
+
for (const dependencyId of needs ?? []) {
|
|
575
|
+
const dependency = run.nodes[dependencyId];
|
|
576
|
+
if (!dependency || dependency.type !== 'prompt' || dependency.output)
|
|
577
|
+
continue;
|
|
578
|
+
const output = await readPromptNodeOutput(run, dependency);
|
|
579
|
+
if (output)
|
|
580
|
+
dependency.output = output;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
234
583
|
// Seed the mutable registry with built-ins. Plugin entries register at boot.
|
|
235
584
|
for (const [type, executor] of Object.entries(BUILTIN_NODE_EXECUTORS)) {
|
|
236
585
|
NODE_EXECUTORS.set(type, executor);
|
|
237
586
|
}
|
|
587
|
+
async function resolveNodeEnv(context, run, node) {
|
|
588
|
+
if (!node.env || Object.keys(node.env).length === 0)
|
|
589
|
+
return undefined;
|
|
590
|
+
const env = {};
|
|
591
|
+
for (const [name, value] of Object.entries(node.env)) {
|
|
592
|
+
if (value.value !== undefined) {
|
|
593
|
+
if (isSecretLikeEnvName(name)) {
|
|
594
|
+
throw new Error(`Env ${name} looks secret-like and must use a credential secret handle instead of a literal value.`);
|
|
595
|
+
}
|
|
596
|
+
env[name] = await renderTemplate(value.value, run);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
if (!value.secret)
|
|
600
|
+
continue;
|
|
601
|
+
const envName = envNameForCredentialRef(value.secret);
|
|
602
|
+
const material = context.runtimeSecretEnv?.[envName] ?? process.env[envName];
|
|
603
|
+
if (!material) {
|
|
604
|
+
throw new Error(`Secret binding ${value.secret} was not materialized for env ${name}. Select it in the workflow/profile and keep runner-local material in ${envName} when using BYO secrets.`);
|
|
605
|
+
}
|
|
606
|
+
env[name] = material;
|
|
607
|
+
}
|
|
608
|
+
return env;
|
|
609
|
+
}
|
|
610
|
+
async function renderContextUpdatePatch(patch, run) {
|
|
611
|
+
if (!patch)
|
|
612
|
+
return undefined;
|
|
613
|
+
const textDigest = patch.text ? digest(await renderTemplate(patch.text, run)) : undefined;
|
|
614
|
+
const files = patch.files
|
|
615
|
+
? await Promise.all(patch.files.map(async (file) => {
|
|
616
|
+
const path = await renderTemplate(file.path, run);
|
|
617
|
+
const operation = file.operation ? await renderTemplate(file.operation, run) : undefined;
|
|
618
|
+
const artifactRef = file.artifact_ref
|
|
619
|
+
? await renderTemplate(file.artifact_ref, run)
|
|
620
|
+
: undefined;
|
|
621
|
+
const beforeDigest = file.before_digest
|
|
622
|
+
? await renderTemplate(file.before_digest, run)
|
|
623
|
+
: undefined;
|
|
624
|
+
const afterDigest = file.after_digest
|
|
625
|
+
? await renderTemplate(file.after_digest, run)
|
|
626
|
+
: undefined;
|
|
627
|
+
const patchDigest = file.patch_digest
|
|
628
|
+
? await renderTemplate(file.patch_digest, run)
|
|
629
|
+
: digest(JSON.stringify({
|
|
630
|
+
path,
|
|
631
|
+
operation,
|
|
632
|
+
artifactRef,
|
|
633
|
+
beforeDigest,
|
|
634
|
+
afterDigest,
|
|
635
|
+
textDigest,
|
|
636
|
+
}));
|
|
637
|
+
return {
|
|
638
|
+
path,
|
|
639
|
+
...(operation ? { operation } : {}),
|
|
640
|
+
...(artifactRef ? { artifact_ref: artifactRef } : {}),
|
|
641
|
+
...(beforeDigest ? { before_digest: beforeDigest } : {}),
|
|
642
|
+
...(afterDigest ? { after_digest: afterDigest } : {}),
|
|
643
|
+
patch_digest: patchDigest,
|
|
644
|
+
};
|
|
645
|
+
}))
|
|
646
|
+
: undefined;
|
|
647
|
+
return {
|
|
648
|
+
...(patch.mode ? { mode: patch.mode } : {}),
|
|
649
|
+
...(textDigest ? { text_digest: textDigest } : {}),
|
|
650
|
+
...(patch.digest ? { digest: await renderTemplate(patch.digest, run) } : {}),
|
|
651
|
+
...(patch.operation ? { operation: await renderTemplate(patch.operation, run) } : {}),
|
|
652
|
+
...(files ? { files } : {}),
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
function patchDigest(patch) {
|
|
656
|
+
if (!patch)
|
|
657
|
+
return null;
|
|
658
|
+
return digest(JSON.stringify(patch));
|
|
659
|
+
}
|
|
660
|
+
function redactedContextUpdatePatch(patch) {
|
|
661
|
+
if (!patch)
|
|
662
|
+
return null;
|
|
663
|
+
return {
|
|
664
|
+
...(typeof patch.mode === 'string' ? { mode: patch.mode } : {}),
|
|
665
|
+
...(typeof patch.operation === 'string' ? { operation: patch.operation } : {}),
|
|
666
|
+
...(typeof patch.digest === 'string' ? { digest: patch.digest } : {}),
|
|
667
|
+
...(typeof patch.text_digest === 'string' ? { text_digest: patch.text_digest } : {}),
|
|
668
|
+
...(Array.isArray(patch.files) ? { files: patch.files } : {}),
|
|
669
|
+
plaintext_patch_persisted: false,
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
async function renderShellInvocation(node, run) {
|
|
673
|
+
if (node.argv && node.argv.length > 0) {
|
|
674
|
+
const argv = await Promise.all(node.argv.map((entry) => renderTemplate(entry, run)));
|
|
675
|
+
return {
|
|
676
|
+
authorityCommand: argv.join(' '),
|
|
677
|
+
digestMaterial: JSON.stringify({ argv }),
|
|
678
|
+
executor: {
|
|
679
|
+
kind: 'argv',
|
|
680
|
+
command: argv[0] ?? null,
|
|
681
|
+
args_count: Math.max(0, argv.length - 1),
|
|
682
|
+
raw_args_persisted: false,
|
|
683
|
+
},
|
|
684
|
+
run: (options) => runArgvNode(argv, options),
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (!node.command) {
|
|
688
|
+
throw new Error(`Shell node must set command or argv.`);
|
|
689
|
+
}
|
|
690
|
+
const command = await renderShellCommandTemplate(node.command, run);
|
|
691
|
+
return {
|
|
692
|
+
authorityCommand: command,
|
|
693
|
+
digestMaterial: command,
|
|
694
|
+
executor: {
|
|
695
|
+
kind: 'shell',
|
|
696
|
+
command: 'sh',
|
|
697
|
+
args: ['-lc'],
|
|
698
|
+
},
|
|
699
|
+
run: (options) => runShellNode(command, options),
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
function buildShellExecutionReceipt(input) {
|
|
703
|
+
const durationMs = input.completedAt !== undefined ? Math.max(0, input.completedAt - input.startedAt) : null;
|
|
704
|
+
return {
|
|
705
|
+
schema: 'viewport.shell_execution_receipt/v1',
|
|
706
|
+
node_id: input.nodeId,
|
|
707
|
+
status: input.status,
|
|
708
|
+
executor: input.executor,
|
|
709
|
+
command_digest: digest(input.commandDigestMaterial),
|
|
710
|
+
command_persisted: false,
|
|
711
|
+
cwd: input.cwd,
|
|
712
|
+
cwd_digest: digest(input.cwd),
|
|
713
|
+
env_keys: Object.keys(input.env ?? {}).sort(),
|
|
714
|
+
env_values_persisted: false,
|
|
715
|
+
timeout_seconds: input.timeoutSeconds ?? null,
|
|
716
|
+
authority: shellAuthorityReceipt(input.run),
|
|
717
|
+
started_at: new Date(input.startedAt).toISOString(),
|
|
718
|
+
completed_at: input.completedAt !== undefined ? new Date(input.completedAt).toISOString() : null,
|
|
719
|
+
duration_ms: durationMs,
|
|
720
|
+
exit_code: input.exitCode ?? null,
|
|
721
|
+
denial: input.denial
|
|
722
|
+
? {
|
|
723
|
+
reason: input.denial.reason,
|
|
724
|
+
detail: input.denial.detail,
|
|
725
|
+
}
|
|
726
|
+
: null,
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
function shellWorkspaceCwdDenial(run, nodeId, cwd) {
|
|
730
|
+
if (isPathWithin(cwd, run.directoryPath))
|
|
731
|
+
return null;
|
|
732
|
+
return {
|
|
733
|
+
schema: 'viewport.workflow_authority_denial/v1',
|
|
734
|
+
reason: 'shell_cwd_outside_run_workspace',
|
|
735
|
+
runId: run.id,
|
|
736
|
+
nodeId,
|
|
737
|
+
detail: `Shell cwd ${cwd} is outside the run worktree.`,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function shellAuthorityReceipt(run) {
|
|
741
|
+
const contract = run.workflowAuthorityContract;
|
|
742
|
+
if (!contract || typeof contract !== 'object') {
|
|
743
|
+
return {
|
|
744
|
+
source: 'legacy_local',
|
|
745
|
+
shell_policy: 'legacy',
|
|
746
|
+
authority_contract_present: false,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const shellPolicy = authorityShellPolicy(contract);
|
|
750
|
+
return {
|
|
751
|
+
source: 'workflow_authority_contract',
|
|
752
|
+
shell_policy: shellPolicy ?? null,
|
|
753
|
+
legacy_command_allowed: authorityLegacyShellCommandAllowed(contract),
|
|
754
|
+
authority_contract_present: true,
|
|
755
|
+
authority_contract_digest: typeof contract.digest === 'string' ? contract.digest : null,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function authorityShellPolicy(contract) {
|
|
759
|
+
const nested = contract['shell'];
|
|
760
|
+
if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
|
|
761
|
+
const policy = nested['policy'];
|
|
762
|
+
if (typeof policy === 'string' && policy.trim() !== '')
|
|
763
|
+
return policy;
|
|
764
|
+
}
|
|
765
|
+
const flat = contract['shell_policy'];
|
|
766
|
+
if (typeof flat === 'string' && flat.trim() !== '')
|
|
767
|
+
return flat;
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
function authorityLegacyShellCommandAllowed(contract) {
|
|
771
|
+
const nested = contract['shell'];
|
|
772
|
+
if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
|
|
773
|
+
const allowed = nested['allow_legacy_command'];
|
|
774
|
+
if (typeof allowed === 'boolean')
|
|
775
|
+
return allowed;
|
|
776
|
+
}
|
|
777
|
+
const flat = contract['shell_allow_legacy_command'];
|
|
778
|
+
return typeof flat === 'boolean' ? flat : false;
|
|
779
|
+
}
|
|
780
|
+
function isPathWithin(candidate, root) {
|
|
781
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
782
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
783
|
+
}
|
|
784
|
+
function digest(value) {
|
|
785
|
+
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
786
|
+
}
|
|
238
787
|
//# sourceMappingURL=node-registry.js.map
|