@controllerai/cli 0.2.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.
Files changed (96) hide show
  1. package/README.md +180 -0
  2. package/dist/api/client.js +330 -0
  3. package/dist/api/client.js.map +1 -0
  4. package/dist/appLinks.js +99 -0
  5. package/dist/appLinks.js.map +1 -0
  6. package/dist/argv.js +124 -0
  7. package/dist/argv.js.map +1 -0
  8. package/dist/commands/agent.js +813 -0
  9. package/dist/commands/agent.js.map +1 -0
  10. package/dist/commands/auth.js +327 -0
  11. package/dist/commands/auth.js.map +1 -0
  12. package/dist/commands/branch.js +441 -0
  13. package/dist/commands/branch.js.map +1 -0
  14. package/dist/commands/connect.js +148 -0
  15. package/dist/commands/connect.js.map +1 -0
  16. package/dist/commands/connection.js +148 -0
  17. package/dist/commands/connection.js.map +1 -0
  18. package/dist/commands/data.js +441 -0
  19. package/dist/commands/data.js.map +1 -0
  20. package/dist/commands/deps.js +63 -0
  21. package/dist/commands/deps.js.map +1 -0
  22. package/dist/commands/doctor.js +102 -0
  23. package/dist/commands/doctor.js.map +1 -0
  24. package/dist/commands/events.js +72 -0
  25. package/dist/commands/events.js.map +1 -0
  26. package/dist/commands/exec.js +306 -0
  27. package/dist/commands/exec.js.map +1 -0
  28. package/dist/commands/expr.js +346 -0
  29. package/dist/commands/expr.js.map +1 -0
  30. package/dist/commands/flow.js +447 -0
  31. package/dist/commands/flow.js.map +1 -0
  32. package/dist/commands/integration.js +102 -0
  33. package/dist/commands/integration.js.map +1 -0
  34. package/dist/commands/node.js +885 -0
  35. package/dist/commands/node.js.map +1 -0
  36. package/dist/commands/organize.js +234 -0
  37. package/dist/commands/organize.js.map +1 -0
  38. package/dist/commands/run.js +437 -0
  39. package/dist/commands/run.js.map +1 -0
  40. package/dist/commands/schema.js +269 -0
  41. package/dist/commands/schema.js.map +1 -0
  42. package/dist/commands/skills.js +1156 -0
  43. package/dist/commands/skills.js.map +1 -0
  44. package/dist/commands/trigger.js +992 -0
  45. package/dist/commands/trigger.js.map +1 -0
  46. package/dist/commands/workflow.js +503 -0
  47. package/dist/commands/workflow.js.map +1 -0
  48. package/dist/config.js +74 -0
  49. package/dist/config.js.map +1 -0
  50. package/dist/context.js +39 -0
  51. package/dist/context.js.map +1 -0
  52. package/dist/errors.js +59 -0
  53. package/dist/errors.js.map +1 -0
  54. package/dist/index.js +125 -0
  55. package/dist/index.js.map +1 -0
  56. package/dist/jsSource.js +32 -0
  57. package/dist/jsSource.js.map +1 -0
  58. package/dist/nodeProps.js +177 -0
  59. package/dist/nodeProps.js.map +1 -0
  60. package/dist/output.js +85 -0
  61. package/dist/output.js.map +1 -0
  62. package/dist/version.js +7 -0
  63. package/dist/version.js.map +1 -0
  64. package/package.json +41 -0
  65. package/skills/controllerai-agents/SKILL.md +169 -0
  66. package/skills/controllerai-agents/agents/openai.yaml +4 -0
  67. package/skills/controllerai-agents/references/instructions-and-versioning.md +76 -0
  68. package/skills/controllerai-ai-nodes/SKILL.md +83 -0
  69. package/skills/controllerai-ai-nodes/agents/openai.yaml +4 -0
  70. package/skills/controllerai-ai-nodes/references/selection-and-testing.md +46 -0
  71. package/skills/controllerai-data/SKILL.md +121 -0
  72. package/skills/controllerai-data/agents/openai.yaml +4 -0
  73. package/skills/controllerai-data/references/data-rails.md +46 -0
  74. package/skills/controllerai-expressions/SKILL.md +115 -0
  75. package/skills/controllerai-expressions/agents/openai.yaml +4 -0
  76. package/skills/controllerai-expressions/references/dsl-and-recipes.md +120 -0
  77. package/skills/controllerai-flow-control/SKILL.md +132 -0
  78. package/skills/controllerai-flow-control/agents/openai.yaml +4 -0
  79. package/skills/controllerai-flow-control/references/patterns.md +61 -0
  80. package/skills/controllerai-integration-actions/SKILL.md +95 -0
  81. package/skills/controllerai-integration-actions/agents/openai.yaml +4 -0
  82. package/skills/controllerai-node-config/SKILL.md +138 -0
  83. package/skills/controllerai-node-config/agents/openai.yaml +4 -0
  84. package/skills/controllerai-node-config/references/prop-and-readiness.md +72 -0
  85. package/skills/controllerai-runs/SKILL.md +109 -0
  86. package/skills/controllerai-runs/agents/openai.yaml +4 -0
  87. package/skills/controllerai-runs/references/debugging.md +53 -0
  88. package/skills/controllerai-start/SKILL.md +44 -0
  89. package/skills/controllerai-start/agents/openai.yaml +4 -0
  90. package/skills/controllerai-triggers/SKILL.md +174 -0
  91. package/skills/controllerai-triggers/agents/openai.yaml +4 -0
  92. package/skills/controllerai-triggers/references/lifecycle.md +54 -0
  93. package/skills/controllerai-workflows/SKILL.md +86 -0
  94. package/skills/controllerai-workflows/agents/openai.yaml +4 -0
  95. package/skills/controllerai-workflows/references/command-map.md +64 -0
  96. package/skills/manifest.json +248 -0
package/dist/config.js ADDED
@@ -0,0 +1,74 @@
1
+ import { homedir } from 'node:os';
2
+ import { dirname, join, resolve } from 'node:path';
3
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
4
+ export const DEFAULT_API_URL = 'https://api.app.getcontroller.ai';
5
+ const defaultConfigDirectory = () => join(homedir(), '.config', 'cai');
6
+ const defaultConfigPath = () => join(defaultConfigDirectory(), 'config.json');
7
+ export const configPath = () => process.env.CAI_CONFIG ?? defaultConfigPath();
8
+ /**
9
+ * Create the config parent securely without changing permissions on a directory
10
+ * the caller already owns. The default cai directory is CLI-owned and may always
11
+ * be hardened; a custom parent may only be chmodded when this call created it.
12
+ */
13
+ const ensureConfigParent = (path) => {
14
+ const parent = dirname(path);
15
+ const parentExisted = existsSync(parent);
16
+ mkdirSync(parent, { recursive: true, mode: 0o700 });
17
+ if (!parentExisted || resolve(parent) === resolve(defaultConfigDirectory())) {
18
+ chmodSync(parent, 0o700);
19
+ }
20
+ };
21
+ const readFile = () => {
22
+ const path = configPath();
23
+ if (!existsSync(path))
24
+ return {};
25
+ try {
26
+ return JSON.parse(readFileSync(path, 'utf8'));
27
+ }
28
+ catch {
29
+ return {};
30
+ }
31
+ };
32
+ /** File config overlaid with env vars. Env always wins. */
33
+ export const loadConfig = () => {
34
+ const file = readFile();
35
+ const envWorkflow = process.env.CAI_WORKFLOW ? Number(process.env.CAI_WORKFLOW) : undefined;
36
+ return {
37
+ apiUrl: (process.env.CAI_API_URL ?? file.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, ''),
38
+ appUrl: file.appUrl,
39
+ token: process.env.CAI_TOKEN ?? file.token,
40
+ tokenKind: process.env.CAI_TOKEN ? detectTokenKind(process.env.CAI_TOKEN) : file.tokenKind,
41
+ workflowId: envWorkflow ?? file.workflowId,
42
+ headStateId: file.headStateId,
43
+ };
44
+ };
45
+ /** Merge a patch into the persisted config file (env overrides are not written). */
46
+ export const saveConfig = (patch) => {
47
+ const path = configPath();
48
+ ensureConfigParent(path);
49
+ const next = { ...readFile(), ...patch };
50
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
51
+ chmodSync(path, 0o600);
52
+ return next;
53
+ };
54
+ export const clearToken = () => {
55
+ const current = readFile();
56
+ const next = { ...current };
57
+ delete next.token;
58
+ delete next.tokenKind;
59
+ delete next.expiresAt;
60
+ const path = configPath();
61
+ ensureConfigParent(path);
62
+ writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
63
+ chmodSync(path, 0o600);
64
+ return next;
65
+ };
66
+ export const detectTokenKind = (token) => token.startsWith('cai_') ? 'cli' : token.startsWith('sk-') ? 'apiKey' : 'jwt';
67
+ export const maskToken = (token) => {
68
+ if (!token)
69
+ return '(none)';
70
+ if (token.length <= 12)
71
+ return `${token.slice(0, 3)}…`;
72
+ return `${token.slice(0, 6)}…${token.slice(-4)}`;
73
+ };
74
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { ApiClient } from './api/client.js';
2
+ import { loadConfig } from './config.js';
3
+ import { userError } from './errors.js';
4
+ export const buildClient = (command) => {
5
+ const config = loadConfig();
6
+ const overrideUrl = globalOpt(command, 'apiUrl');
7
+ if (typeof overrideUrl === 'string' && overrideUrl) {
8
+ config.apiUrl = overrideUrl.replace(/\/+$/, '');
9
+ }
10
+ return { config, api: new ApiClient(config) };
11
+ };
12
+ /** Walk to the root program to read a global flag from any depth of subcommand. */
13
+ export const globalOpt = (command, name) => {
14
+ let node = command;
15
+ while (node) {
16
+ const value = node.opts()[name];
17
+ if (value !== undefined)
18
+ return value;
19
+ node = node.parent;
20
+ }
21
+ return undefined;
22
+ };
23
+ export const resolveWorkflowId = (command, config) => {
24
+ const flag = globalOpt(command, 'workflow');
25
+ const raw = flag ?? config.workflowId;
26
+ if (raw === undefined || raw === null || raw === '') {
27
+ throw userError('No workflow selected. Pass --workflow <id>, set CAI_WORKFLOW, or run `cai use <id>`.', 'NO_WORKFLOW');
28
+ }
29
+ const id = Number(raw);
30
+ if (!Number.isInteger(id) || id <= 0) {
31
+ throw userError(`Invalid workflow id: ${String(raw)}`, 'BAD_WORKFLOW_ID');
32
+ }
33
+ return id;
34
+ };
35
+ export const requireContext = (command) => {
36
+ const { config, api } = buildClient(command);
37
+ return { config, api, workflowId: resolveWorkflowId(command, config) };
38
+ };
39
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAa,UAAU,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AASvC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAyC,EAAE;IACrF,MAAM,MAAM,GAAG,UAAU,EAAE,CAAA;IAC3B,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;IAChD,IAAI,OAAO,WAAW,KAAK,QAAQ,IAAI,WAAW,EAAE,CAAC;QACnD,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,SAAS,CAAC,MAAM,CAAC,EAAE,CAAA;AAC/C,CAAC,CAAA;AAED,mFAAmF;AACnF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAgB,EAAE,IAAY,EAAW,EAAE;IACnE,IAAI,IAAI,GAAmB,OAAO,CAAA;IAClC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,KAAK,GAAI,IAAI,CAAC,IAAI,EAA8B,CAAC,IAAI,CAAC,CAAA;QAC5D,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;QACrC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAgB,EAAE,MAAiB,EAAU,EAAE;IAC/E,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAC3C,MAAM,GAAG,GAAG,IAAI,IAAI,MAAM,CAAC,UAAU,CAAA;IACrC,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QACpD,MAAM,SAAS,CACb,sFAAsF,EACtF,aAAa,CACd,CAAA;IACH,CAAC;IACD,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;QACrC,MAAM,SAAS,CAAC,wBAAwB,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,iBAAiB,CAAC,CAAA;IAC3E,CAAC;IACD,OAAO,EAAE,CAAA;AACX,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAgB,EAAkB,EAAE;IACjE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAC5C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAA;AACxE,CAAC,CAAA"}
package/dist/errors.js ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Exit codes are part of the CLI contract:
3
+ * 0 ok | 1 user/validation | 2 conflict (OCC) | 3 auth | 4 network
4
+ */
5
+ export const EXIT = {
6
+ ok: 0,
7
+ user: 1,
8
+ conflict: 2,
9
+ auth: 3,
10
+ network: 4,
11
+ };
12
+ export class CaiError extends Error {
13
+ exitCode;
14
+ code;
15
+ detail;
16
+ /** Set when the backend scrubbed a 5xx and the real cause is only in its log. */
17
+ scrubbed;
18
+ constructor(message, opts = {}) {
19
+ // Every auth-class failure must be actionable without reading docs.
20
+ const exitCode = opts.exitCode ?? EXIT.user;
21
+ const actionable = exitCode === EXIT.auth && !/cai auth login/.test(message)
22
+ ? `${message} Run \`cai auth login\` to re-authenticate.`
23
+ : message;
24
+ super(actionable);
25
+ this.name = 'CaiError';
26
+ this.exitCode = exitCode;
27
+ this.code = opts.code ?? 'ERROR';
28
+ this.detail = opts.detail;
29
+ this.scrubbed = opts.scrubbed ?? false;
30
+ }
31
+ }
32
+ export const userError = (message, code = 'VALIDATION_ERROR') => new CaiError(message, { exitCode: EXIT.user, code });
33
+ export const authError = (message, code = 'UNAUTHORIZED') => new CaiError(message, { exitCode: EXIT.auth, code });
34
+ export const conflictError = (message, code = 'STATE_CONFLICT') => new CaiError(message, { exitCode: EXIT.conflict, code });
35
+ export const networkError = (message, code = 'NETWORK_ERROR') => new CaiError(message, { exitCode: EXIT.network, code });
36
+ /**
37
+ * Map an HTTP status + backend error code onto a CLI exit code.
38
+ * 401/403 -> auth, 409 (or STATE_CONFLICT) -> conflict, 4xx -> user, 5xx -> network.
39
+ */
40
+ export const exitCodeForStatus = (status, backendCode) => {
41
+ if (backendCode === 'STATE_CONFLICT')
42
+ return EXIT.conflict;
43
+ if (status === 401 || status === 403)
44
+ return EXIT.auth;
45
+ if (status === 409)
46
+ return EXIT.conflict;
47
+ if (status >= 500)
48
+ return EXIT.network;
49
+ if (status >= 400)
50
+ return EXIT.user;
51
+ return EXIT.user;
52
+ };
53
+ /** The backend's error middleware replaces every 5xx cause with this string. */
54
+ export const SCRUBBED_MESSAGES = new Set(['Internal server error', 'INTERNAL_SERVER_ERROR']);
55
+ export const isScrubbed = (status, message) => status >= 500 && SCRUBBED_MESSAGES.has(message.trim());
56
+ export const SCRUBBED_HINT = 'The backend scrubbed this 5xx: the response carries no cause, and the CLI will not invent one. ' +
57
+ 'The real stack trace is only in the backend container log ' +
58
+ '(feature stacks: .feature-stacks/<name>/be.log; docker: `docker logs <be-container>`).';
59
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env node
2
+ import { realpathSync } from 'node:fs';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { Command } from 'commander';
5
+ import { addGlobalOptions, applyExitOverride, collectFlags, explainUnknownOptions, normalizeArgv, } from './argv.js';
6
+ import { globalOpt } from './context.js';
7
+ import { CaiError, EXIT } from './errors.js';
8
+ import { emitError, setCommandPath, setJsonMode } from './output.js';
9
+ import { registerAuth } from './commands/auth.js';
10
+ import { registerAgent } from './commands/agent.js';
11
+ import { registerBranch } from './commands/branch.js';
12
+ import { registerConnection } from './commands/connection.js';
13
+ import { registerData } from './commands/data.js';
14
+ import { registerDeps } from './commands/deps.js';
15
+ import { registerDoctor } from './commands/doctor.js';
16
+ import { registerEvents } from './commands/events.js';
17
+ import { registerExec } from './commands/exec.js';
18
+ import { registerExpr } from './commands/expr.js';
19
+ import { registerFlow } from './commands/flow.js';
20
+ import { registerIntegration } from './commands/integration.js';
21
+ import { registerNode } from './commands/node.js';
22
+ import { registerOrganize } from './commands/organize.js';
23
+ import { registerRun } from './commands/run.js';
24
+ import { registerSchema } from './commands/schema.js';
25
+ import { registerTrigger } from './commands/trigger.js';
26
+ import { registerUse, registerWorkflow } from './commands/workflow.js';
27
+ import { registerConnect } from './commands/connect.js';
28
+ import { registerSkills } from './commands/skills.js';
29
+ import { CLI_VERSION } from './version.js';
30
+ export { CLI_VERSION } from './version.js';
31
+ export const buildProgram = () => {
32
+ const program = new Command();
33
+ program
34
+ .name('cai')
35
+ .description('Build and operate Controller AI workflows and agents')
36
+ .version(CLI_VERSION)
37
+ // No default on --json: absent must read as `undefined` so globalOpt() keeps
38
+ // walking up. A `false` here would shadow a --json given after the
39
+ // subcommand, and vice versa.
40
+ .option('--json', 'machine-readable output on stdout')
41
+ .option('--workflow <id>', 'workflow id (overrides `cai use` and CAI_WORKFLOW)')
42
+ .option('--api-url <url>', 'backend base URL (overrides CAI_API_URL)')
43
+ .option('--id <value>', 'positional id, for ids that begin with "-"')
44
+ .showHelpAfterError()
45
+ .enablePositionalOptions();
46
+ registerAuth(program);
47
+ registerConnect(program);
48
+ registerSkills(program);
49
+ registerDoctor(program);
50
+ registerAgent(program);
51
+ registerUse(program);
52
+ registerWorkflow(program);
53
+ registerFlow(program);
54
+ registerIntegration(program);
55
+ registerNode(program);
56
+ registerExpr(program);
57
+ registerRun(program);
58
+ registerExec(program);
59
+ registerConnection(program);
60
+ registerBranch(program);
61
+ registerData(program);
62
+ registerDeps(program);
63
+ registerEvents(program);
64
+ registerSchema(program);
65
+ registerTrigger(program);
66
+ registerOrganize(program);
67
+ // The three global flags are documented as working on every command, so they
68
+ // are declared on every command — commander only parses an option on the
69
+ // command that declares it, and `cai flow list --json` must work as readily as
70
+ // `cai --json flow list`.
71
+ addGlobalOptions(program);
72
+ program.hook('preAction', (_thisCommand, actionCommand) => {
73
+ // Read from the LEAF upwards so the flag is honoured in either position.
74
+ setJsonMode(Boolean(globalOpt(actionCommand, 'json')));
75
+ const path = [];
76
+ let node = actionCommand;
77
+ while (node) {
78
+ path.unshift(node.name());
79
+ node = node.parent;
80
+ }
81
+ setCommandPath(path.join(' '));
82
+ });
83
+ return program;
84
+ };
85
+ const main = async () => {
86
+ const program = buildProgram();
87
+ explainUnknownOptions(program);
88
+ applyExitOverride(program, error => {
89
+ // commander's own exits (help, version, bad usage) keep their codes
90
+ process.exit(error.exitCode === 0 ? 0 : EXIT.user);
91
+ });
92
+ await program.parseAsync(normalizeArgv(process.argv, collectFlags(program)));
93
+ };
94
+ // Only drive the CLI when this module is the process entry point; the test
95
+ // suite imports buildProgram() from here and must not trigger a parse+exit.
96
+ // `import.meta.url` is always the REAL path, so a symlinked bin (`npm link`, or a
97
+ // PATH shim that links dist/index.js) compares unequal to argv[1] and the CLI exits
98
+ // 0 having printed nothing — silently, which is the worst possible failure. Resolve
99
+ // argv[1] through realpath before comparing.
100
+ const isEntryPoint = () => {
101
+ const entry = process.argv[1];
102
+ if (!entry)
103
+ return false;
104
+ let resolved = entry;
105
+ try {
106
+ resolved = realpathSync(entry);
107
+ }
108
+ catch {
109
+ // not a real file (bundled/virtual entry) — fall back to the raw argv value
110
+ }
111
+ return import.meta.url === pathToFileURL(resolved).href;
112
+ };
113
+ if (isEntryPoint()) {
114
+ main().catch(error => {
115
+ const caiError = error instanceof CaiError
116
+ ? error
117
+ : new CaiError(error instanceof Error ? error.message : String(error), {
118
+ exitCode: EXIT.user,
119
+ code: 'UNEXPECTED',
120
+ });
121
+ emitError(caiError);
122
+ process.exit(caiError.exitCode);
123
+ });
124
+ }
125
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { userError } from './errors.js';
3
+ const readUtf8 = (source, encoding) => readFileSync(source, encoding);
4
+ /**
5
+ * Resolve an inline expression or a UTF-8 file before any caller performs a
6
+ * write. `-` deliberately maps to stdin's file descriptor.
7
+ */
8
+ export const resolveJsSource = (opts, settings = {}, reader = readUtf8) => {
9
+ const inlineFlag = settings.inlineFlag ?? '--js';
10
+ const fileFlag = settings.fileFlag ?? '--js-file';
11
+ const hasInline = opts.js !== undefined;
12
+ const hasFile = opts.jsFile !== undefined;
13
+ if (hasInline && hasFile) {
14
+ throw userError(`${inlineFlag} and ${fileFlag} are mutually exclusive.`, 'CONFLICTING_JS_SOURCE');
15
+ }
16
+ if (hasInline)
17
+ return opts.js;
18
+ if (hasFile) {
19
+ try {
20
+ return reader(opts.jsFile === '-' ? 0 : opts.jsFile, 'utf8');
21
+ }
22
+ catch (error) {
23
+ throw userError(`${fileFlag} "${opts.jsFile}" is not a readable UTF-8 file: ` +
24
+ `${error instanceof Error ? error.message : error}`, 'UNREADABLE_JS_FILE');
25
+ }
26
+ }
27
+ if (settings.required !== false) {
28
+ throw userError(`Pass ${inlineFlag} <expression> or ${fileFlag} <path|->.`, 'MISSING_JS_SOURCE');
29
+ }
30
+ return undefined;
31
+ };
32
+ //# sourceMappingURL=jsSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsSource.js","sourceRoot":"","sources":["../src/jsSource.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAevC,MAAM,QAAQ,GAAe,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEjF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,IAAqB,EACrB,WAA6B,EAAE,EAC/B,SAAqB,QAAQ,EACT,EAAE;IACtB,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,IAAI,MAAM,CAAA;IAChD,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,WAAW,CAAA;IACjD,MAAM,SAAS,GAAG,IAAI,CAAC,EAAE,KAAK,SAAS,CAAA;IACvC,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,CAAA;IAEzC,IAAI,SAAS,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,SAAS,CACb,GAAG,UAAU,QAAQ,QAAQ,0BAA0B,EACvD,uBAAuB,CACxB,CAAA;IACH,CAAC;IACD,IAAI,SAAS;QAAE,OAAO,IAAI,CAAC,EAAE,CAAA;IAC7B,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAO,EAAE,MAAM,CAAC,CAAA;QAC/D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,SAAS,CACb,GAAG,QAAQ,KAAK,IAAI,CAAC,MAAM,kCAAkC;gBAC3D,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,EACrD,oBAAoB,CACrB,CAAA;QACH,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;QAChC,MAAM,SAAS,CAAC,QAAQ,UAAU,oBAAoB,QAAQ,YAAY,EAAE,mBAAmB,CAAC,CAAA;IAClG,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA"}
@@ -0,0 +1,177 @@
1
+ const NODE_PROP_WRITE_KEYS = new Set([
2
+ 'value',
3
+ 'valueLabel',
4
+ 'valueJs',
5
+ 'expressionValue',
6
+ 'expressionValueJs',
7
+ 'useExpression',
8
+ ]);
9
+ const isExplicitNodePropWrite = (value) => Boolean(value) &&
10
+ typeof value === 'object' &&
11
+ !Array.isArray(value) &&
12
+ Object.keys(value).some(key => NODE_PROP_WRITE_KEYS.has(key));
13
+ /**
14
+ * `add_node.props` is a map of per-prop write objects. Humans and skills naturally
15
+ * pass a direct map (`{"channel":"C123"}`), so accept both forms and normalize
16
+ * direct values to the backend's exact `{value: ...}` contract.
17
+ */
18
+ export const normalizeNodePropWrites = (props) => Object.fromEntries(Object.entries(props).map(([key, value]) => [
19
+ key,
20
+ isExplicitNodePropWrite(value) ? value : { value },
21
+ ]));
22
+ export const nodePropsFromPayload = (payload) => {
23
+ if (Array.isArray(payload))
24
+ return payload;
25
+ if (!payload || typeof payload !== 'object')
26
+ return [];
27
+ const record = payload;
28
+ for (const key of ['props', 'data', 'summary']) {
29
+ if (Array.isArray(record[key]))
30
+ return record[key];
31
+ }
32
+ return [];
33
+ };
34
+ export const hasConfiguredPropValue = (prop) => ![prop.value, prop.valueJs, prop.expressionValue, prop.expressionValueJs].every(value => value === undefined || value === null || value === '');
35
+ // This mirrors the backend's TEXT_FAMILY_PROP_INPUT_TYPES. Callers always send
36
+ // valueJs; the backend chooses the stored field and get_node_props decompiles it
37
+ // back into one of these two readback channels.
38
+ const EXPRESSION_STORED_IN_VALUE = new Set([
39
+ 'TEXT',
40
+ 'SHORT_TEXT',
41
+ 'LONG_TEXT',
42
+ 'JSON',
43
+ 'SQL',
44
+ 'CUSTOM_DATA',
45
+ ]);
46
+ export const expressionReadbackChannel = (prop) => Boolean(prop.allowsExpressionMode) &&
47
+ !EXPRESSION_STORED_IN_VALUE.has(String(prop.inputType ?? ''))
48
+ ? 'expressionValueJs'
49
+ : 'valueJs';
50
+ const shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
51
+ const propName = (prop) => prop.name ?? prop.key;
52
+ const propNextCommand = (nodeId, prop, fallbackName) => {
53
+ const name = propName(prop ?? {}) ?? fallbackName;
54
+ if (prop?.remoteOptions) {
55
+ return (`cai node options ${nodeId} --prop ${name} --query "<option label>"; then ` +
56
+ `cai node set ${nodeId} --prop ${name} --value <exact-returned-value> ` +
57
+ '--label "<exact-returned-label>"');
58
+ }
59
+ const options = prop?.options ?? [];
60
+ if (options.length > 0) {
61
+ const values = options
62
+ .map(option => option.value ?? option.id)
63
+ .filter(value => value !== undefined);
64
+ const first = values[0];
65
+ if (first !== undefined) {
66
+ const multi = [
67
+ 'MULTI_SELECT',
68
+ 'MULTI_SELECT_DROPDOWN',
69
+ 'STATIC_MULTI_SELECT_DROPDOWN',
70
+ ].includes(String(prop?.inputType ?? ''));
71
+ const selected = multi ? [first] : first;
72
+ const choices = options
73
+ .map(option => {
74
+ const value = option.value ?? option.id;
75
+ const label = option.label ?? option.name ?? value;
76
+ return `${JSON.stringify(value)} (${String(label)})`;
77
+ })
78
+ .join(', ');
79
+ return `cai node set ${nodeId} --prop ${name} --value ${shellQuote(typeof selected === 'string' ? selected : JSON.stringify(selected))}; advertised choices: ${choices}`;
80
+ }
81
+ }
82
+ const supportsExpression = EXPRESSION_STORED_IN_VALUE.has(String(prop?.inputType ?? '')) ||
83
+ Boolean(prop?.allowsExpressionMode);
84
+ if (supportsExpression) {
85
+ return (`Choose by intent for ${name}: literal -> cai node set ${nodeId} --prop ${name} ` +
86
+ `--value <value>; runtime-derived -> cai expr context ${nodeId} --prop ${name}, then ` +
87
+ `cai expr set ${nodeId} --prop ${name} --js <expression>`);
88
+ }
89
+ return `cai node set ${nodeId} --prop ${name} --value <value>`;
90
+ };
91
+ /** Pure readiness projection used by `cai node status` and its tests. */
92
+ export const buildNodeReadiness = (nodeId, record, props) => {
93
+ const connectionId = record.connectionId ?? record.data?.connectionId ?? null;
94
+ const requireAuth = Boolean(record.requireAuth ?? record.data?.requireAuth);
95
+ const missingConnection = requireAuth && !connectionId;
96
+ const pendingReloadProps = props
97
+ .filter(prop => prop.reloadProps && prop.enabled !== false && !hasConfiguredPropValue(prop))
98
+ .map(prop => prop.name ?? prop.key)
99
+ .filter((name) => Boolean(name));
100
+ const pendingReloadSet = new Set(pendingReloadProps);
101
+ const missingRequiredProps = props
102
+ .filter(prop => prop.required &&
103
+ prop.enabled !== false &&
104
+ !hasConfiguredPropValue(prop) &&
105
+ !pendingReloadSet.has(prop.name ?? prop.key ?? ''))
106
+ .map(prop => prop.name ?? prop.key)
107
+ .filter((name) => Boolean(name));
108
+ const propsByName = new Map(props
109
+ .map(prop => [propName(prop), prop])
110
+ .filter((entry) => Boolean(entry[0])));
111
+ const outputs = record.outputs ?? record.data?.outputs ?? [];
112
+ const outputPortTypes = (Array.isArray(outputs) ? outputs : [])
113
+ .map((output) => output?.dataType)
114
+ .filter((value) => typeof value === 'string' && value.length > 0);
115
+ // get_node.outputs reports connection-port wrappers such as
116
+ // output.node.<id>#data. That says where a value came from, not what the
117
+ // process returned, and must never make downstream expression readiness true.
118
+ const processOutputType = record.processOutput?.dataType ?? record.data?.processOutput?.dataType ?? null;
119
+ const componentOutputType = record.resolvedOutputType ?? record.componentOutputType ?? null;
120
+ const outputTypes = [processOutputType ?? componentOutputType].filter((value) => typeof value === 'string' && value.length > 0);
121
+ const outputTypeSource = processOutputType
122
+ ? 'processOutput'
123
+ : componentOutputType
124
+ ? 'component'
125
+ : outputPortTypes.length
126
+ ? 'port-wrapper'
127
+ : 'none';
128
+ const isUnresolvedType = (type) => /(^|[.#])any$|unknown|unresolved/i.test(type) ||
129
+ /^node\./.test(type) ||
130
+ /^output\.node\./.test(type);
131
+ const outputTypeStatus = processOutputType
132
+ ? outputTypes.some(isUnresolvedType)
133
+ ? 'unresolved'
134
+ : 'concrete'
135
+ : componentOutputType || outputPortTypes.length > 0
136
+ ? 'unresolved'
137
+ : 'none';
138
+ const configurationBlockers = [
139
+ ...(missingConnection ? ['connection'] : []),
140
+ ...missingRequiredProps.map(name => `required prop: ${name}`),
141
+ ...pendingReloadProps.map(name => `reload selector: ${name}`),
142
+ ];
143
+ const blockedBy = [
144
+ ...configurationBlockers,
145
+ ...(outputTypeStatus === 'unresolved' ? ['output type'] : []),
146
+ ];
147
+ const readyForRun = configurationBlockers.length === 0;
148
+ const readyForExpressions = readyForRun;
149
+ const readyForDownstreamExpressions = readyForRun && outputTypeStatus === 'concrete';
150
+ const next = missingConnection
151
+ ? `cai connection list --node ${record.nodeSlug ?? record.data?.nodeSlug ?? '<nodeSlug>'}; then cai node set-connection ${nodeId} --connection <id> (if none exist, run cai connection connect ${record.nodeSlug ?? record.data?.nodeSlug ?? '<nodeSlug>'} and list again)`
152
+ : missingRequiredProps.length
153
+ ? propNextCommand(nodeId, propsByName.get(missingRequiredProps[0]), missingRequiredProps[0])
154
+ : pendingReloadProps.length
155
+ ? propNextCommand(nodeId, propsByName.get(pendingReloadProps[0]), pendingReloadProps[0])
156
+ : outputTypeStatus === 'unresolved'
157
+ ? `cai run node ${nodeId} --inputs <values.json>`
158
+ : `cai expr context ${nodeId} --prop <prop>`;
159
+ return {
160
+ nodeId,
161
+ requireAuth,
162
+ connectionId,
163
+ missingConnection,
164
+ missingRequiredProps,
165
+ pendingReloadProps,
166
+ outputTypeStatus,
167
+ outputTypes,
168
+ outputPortTypes,
169
+ outputTypeSource,
170
+ readyForExpressions,
171
+ readyForRun,
172
+ readyForDownstreamExpressions,
173
+ blockedBy,
174
+ next,
175
+ };
176
+ };
177
+ //# sourceMappingURL=nodeProps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nodeProps.js","sourceRoot":"","sources":["../src/nodeProps.ts"],"names":[],"mappings":"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"}
package/dist/output.js ADDED
@@ -0,0 +1,85 @@
1
+ import { SCRUBBED_HINT } from './errors.js';
2
+ let jsonMode = false;
3
+ let commandPath = 'cai';
4
+ const warnings = [];
5
+ export const setJsonMode = (value) => {
6
+ jsonMode = value;
7
+ };
8
+ export const isJsonMode = () => jsonMode;
9
+ export const setCommandPath = (value) => {
10
+ commandPath = value;
11
+ };
12
+ /**
13
+ * Never let a silent null pass unremarked — the engine turns several real
14
+ * failures (unknown op on a CType, missing injected source, a null resolved
15
+ * config) into a quiet null. Warnings go to stderr so `--json` stdout stays
16
+ * pipeable, and are also embedded in the JSON envelope.
17
+ */
18
+ export const warn = (message) => {
19
+ warnings.push(message);
20
+ process.stderr.write(`warning: ${message}\n`);
21
+ };
22
+ export const emit = (data, human) => {
23
+ if (jsonMode) {
24
+ const envelope = { ok: true, command: commandPath, data };
25
+ if (warnings.length)
26
+ envelope.warnings = [...warnings];
27
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
28
+ return;
29
+ }
30
+ human();
31
+ };
32
+ export const emitError = (error) => {
33
+ if (jsonMode) {
34
+ const envelope = {
35
+ ok: false,
36
+ command: commandPath,
37
+ error: {
38
+ code: error.code,
39
+ message: error.message,
40
+ ...(error.scrubbed ? { scrubbed: true } : {}),
41
+ ...(error.detail !== undefined ? { detail: error.detail } : {}),
42
+ },
43
+ };
44
+ if (warnings.length)
45
+ envelope.warnings = [...warnings];
46
+ process.stdout.write(`${JSON.stringify(envelope, null, 2)}\n`);
47
+ return;
48
+ }
49
+ process.stderr.write(`error: ${error.message}\n`);
50
+ if (error.scrubbed)
51
+ process.stderr.write(`\n${SCRUBBED_HINT}\n`);
52
+ };
53
+ const cell = (value) => {
54
+ if (value === null || value === undefined)
55
+ return '-';
56
+ if (typeof value === 'object')
57
+ return JSON.stringify(value);
58
+ return String(value);
59
+ };
60
+ /** Left-aligned fixed-width table. Empty rows print a dash line, never nothing. */
61
+ export const table = (columns, rows) => {
62
+ if (rows.length === 0)
63
+ return '(none)';
64
+ const body = rows.map(r => r.map(cell));
65
+ const widths = columns.map((c, i) => Math.max(c.length, ...body.map(r => (r[i] ?? '').length)));
66
+ const line = (cells) => cells
67
+ .map((c, i) => c.padEnd(widths[i]))
68
+ .join(' ')
69
+ .trimEnd();
70
+ return [line(columns), line(widths.map(w => '-'.repeat(w))), ...body.map(line)].join('\n');
71
+ };
72
+ export const printTable = (columns, rows) => {
73
+ process.stdout.write(`${table(columns, rows)}\n`);
74
+ };
75
+ export const printKeyValues = (pairs) => {
76
+ const width = Math.max(...pairs.map(([k]) => k.length));
77
+ for (const [key, value] of pairs) {
78
+ process.stdout.write(`${key.padEnd(width)} ${cell(value)}\n`);
79
+ }
80
+ };
81
+ export const truncate = (value, max = 80) => {
82
+ const text = cell(value);
83
+ return text.length > max ? `${text.slice(0, max - 1)}…` : text;
84
+ };
85
+ //# sourceMappingURL=output.js.map