@worca/app 1.0.0 → 1.2.0-rc.1
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 +30 -9
- package/agents/clarify.meta.json +4 -4
- package/agents/decomposer.meta.json +5 -5
- package/agents/implementer.meta.json +15 -5
- package/agents/manualTestsChecklist.meta.json +5 -4
- package/agents/manualWebUiTesting.meta.json +9 -4
- package/agents/planReviewer.meta.json +12 -4
- package/agents/planner.meta.json +12 -5
- package/agents/refiner.meta.json +15 -4
- package/agents/reviewer.meta.json +14 -4
- package/agents/worca-cc-clarify.md +7 -0
- package/agents/worca-cc-code-reviewer.md +11 -6
- package/agents/worca-cc-decomposer.md +7 -0
- package/agents/worca-cc-implementer.md +9 -0
- package/agents/worca-cc-manual-tests-checklist.md +8 -5
- package/agents/worca-cc-manual-web-ui-testing.md +10 -6
- package/agents/worca-cc-plan-refiner.md +11 -6
- package/agents/worca-cc-plan-reviewer.md +10 -7
- package/agents/worca-cc-planner.md +9 -0
- package/agents/worca-cc-workspace-reviewer.md +11 -4
- package/agents/worca-cc-workspace-scanner.md +8 -4
- package/agents/workspaceReviewer.meta.json +15 -4
- package/agents/workspaceScanner.meta.json +5 -4
- package/package.json +8 -2
- package/skills/worca/SKILL.md +5 -5
- package/src/cli/render.mjs +148 -0
- package/src/cli/worca-cc.mjs +386 -56
- package/src/core/agent-gen.mjs +69 -31
- package/src/core/agent-registry.mjs +124 -144
- package/src/core/agent-store.mjs +164 -4
- package/src/core/artifacts.mjs +199 -23
- package/src/core/ask/attachment-kind.mjs +95 -0
- package/src/core/ask/catalog.mjs +111 -0
- package/src/core/ask/comment-deps.mjs +55 -0
- package/src/core/ask/events.mjs +545 -0
- package/src/core/ask/follow.mjs +113 -0
- package/src/core/ask/git-allowlist.mjs +226 -0
- package/src/core/ask/limits.mjs +57 -0
- package/src/core/ask/mcp-stdio.mjs +135 -0
- package/src/core/ask/models.mjs +125 -0
- package/src/core/ask/prompt.mjs +286 -0
- package/src/core/ask/proposal.mjs +170 -0
- package/src/core/ask/redact.mjs +30 -0
- package/src/core/ask/spawn.mjs +156 -0
- package/src/core/ask/store.mjs +438 -0
- package/src/core/ask/tool-deps.mjs +87 -0
- package/src/core/ask/tools.mjs +879 -0
- package/src/core/ask/turn.mjs +462 -0
- package/src/core/ask/worktree-deps.mjs +27 -0
- package/src/core/ask/worktrees.mjs +285 -0
- package/src/core/chat/command-router.mjs +28 -7
- package/src/core/chat/notifier.mjs +6 -1
- package/src/core/chat/renderers.mjs +15 -8
- package/src/core/claude-runner.mjs +541 -62
- package/src/core/config.mjs +310 -44
- package/src/core/cost-budget.mjs +29 -2
- package/src/core/db.mjs +773 -53
- package/src/core/diff-anchor.mjs +213 -0
- package/src/core/diff-comments.mjs +273 -0
- package/src/core/engine-select.mjs +32 -0
- package/src/core/failure-policy.mjs +201 -0
- package/src/core/git-info.mjs +49 -10
- package/src/core/graph/builtin-workflows.mjs +51 -0
- package/src/core/graph/executor.mjs +894 -0
- package/src/core/graph/registry-ports.mjs +12 -0
- package/src/core/graph/scheduler.mjs +1072 -0
- package/src/core/graph/seed-templates.mjs +318 -0
- package/src/core/host-guard.mjs +271 -0
- package/src/core/model-env.mjs +180 -8
- package/src/core/model-test.mjs +79 -0
- package/src/core/orchestrator.mjs +994 -4097
- package/src/core/overview-agent.mjs +15 -3
- package/src/core/phases.mjs +208 -537
- package/src/core/pipeline-delete.mjs +13 -2
- package/src/core/plugin-api.mjs +8 -3
- package/src/core/plugin-config.mjs +178 -28
- package/src/core/plugin-inventory.mjs +6 -2
- package/src/core/plugin-manifest.mjs +199 -11
- package/src/core/plugin-models.mjs +1 -0
- package/src/core/plugin-repo.mjs +16 -4
- package/src/core/plugin-shim-child.mjs +9 -3
- package/src/core/plugin-shim.mjs +80 -17
- package/src/core/plugin-store.mjs +236 -29
- package/src/core/plugin-workflows.mjs +90 -41
- package/src/core/preflight.mjs +135 -3
- package/src/core/projects.mjs +7 -5
- package/src/core/protocol.mjs +8 -35
- package/src/core/recoverable-error.mjs +1 -1
- package/src/core/run-harness.mjs +3934 -0
- package/src/core/run-manifest.mjs +5 -1
- package/src/core/settings.mjs +184 -13
- package/src/core/skills.mjs +10 -3
- package/src/core/source-bindings.mjs +175 -0
- package/src/core/sources.mjs +87 -25
- package/src/core/stats.mjs +25 -6
- package/src/core/title.mjs +51 -4
- package/src/core/workflows.mjs +358 -259
- package/src/core/workspace-scan.mjs +4 -0
- package/src/core/worktree.mjs +98 -7
- package/src/shared/graph/agent-meta.mjs +278 -0
- package/src/shared/graph/constants.mjs +105 -0
- package/src/shared/graph/geometry.mjs +157 -0
- package/src/shared/graph/layout.mjs +134 -0
- package/src/shared/graph/loops.mjs +130 -0
- package/src/shared/graph/manifest.mjs +257 -0
- package/src/shared/graph/ports.mjs +153 -0
- package/src/shared/graph/route.mjs +397 -0
- package/src/shared/graph/template.mjs +165 -0
- package/src/shared/graph/thumbnail.mjs +67 -0
- package/src/shared/graph/validate.mjs +491 -0
- package/src/shared/graph/verdict.mjs +41 -0
- package/ui/public/app.js +4240 -1682
- package/ui/public/ask-markdown.mjs +145 -0
- package/ui/public/ask-model.mjs +317 -0
- package/ui/public/ask-panel.mjs +2129 -0
- package/ui/public/chat-settings-view.mjs +6 -2
- package/ui/public/diff-view.mjs +66 -11
- package/ui/public/file-tree.mjs +305 -0
- package/ui/public/graph/composer.mjs +889 -0
- package/ui/public/graph/inspector.mjs +183 -0
- package/ui/public/graph/model.mjs +37 -0
- package/ui/public/graph/palette.mjs +144 -0
- package/ui/public/graph/run-decor.mjs +410 -0
- package/ui/public/graph/run-hosts.mjs +201 -0
- package/ui/public/graph/save-dialog.mjs +56 -0
- package/ui/public/graph/view.mjs +858 -0
- package/ui/public/guardrails-view.mjs +4 -2
- package/ui/public/hljs-loader.mjs +180 -0
- package/ui/public/index.html +311 -265
- package/ui/public/log-filter.mjs +22 -4
- package/ui/public/log-line.mjs +45 -19
- package/ui/public/models-view.mjs +171 -9
- package/ui/public/plugins-view.mjs +106 -4
- package/ui/public/source-pane.mjs +190 -8
- package/ui/public/stats-view.mjs +81 -1
- package/ui/public/style.css +1487 -229
- package/ui/public/syntax-highlight.mjs +270 -0
- package/ui/public/thinking-orb.mjs +110 -0
- package/ui/server.mjs +1894 -104
- package/src/core/channels.mjs +0 -302
- package/src/core/runners.mjs +0 -167
- package/src/core/workflow-validator.mjs +0 -185
- package/ui/public/composer-core.mjs +0 -211
package/src/core/plugin-repo.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { execFile } from 'node:child_process';
|
|
11
11
|
import { promisify } from 'node:util';
|
|
12
12
|
import { createHash } from 'node:crypto';
|
|
13
|
-
import { mkdirSync, existsSync, rmSync } from 'node:fs';
|
|
13
|
+
import { mkdirSync, existsSync, rmSync, unlinkSync } from 'node:fs';
|
|
14
14
|
import { mkdtemp, rm } from 'node:fs/promises';
|
|
15
15
|
import { tmpdir } from 'node:os';
|
|
16
16
|
import { join, dirname } from 'node:path';
|
|
@@ -160,6 +160,9 @@ export async function addPluginRepo(repoUrl, { exec = defaultExec } = {}) {
|
|
|
160
160
|
}
|
|
161
161
|
const res = normalizeManifest(raw, { dir: subdir || '.' });
|
|
162
162
|
if (!res.ok) { warnings.push(...res.errors); continue; }
|
|
163
|
+
// Degraded-but-usable manifests (e.g. a reserved model-env key stripped)
|
|
164
|
+
// stay discoverable; the notice still reaches the marketplace panel.
|
|
165
|
+
warnings.push(...res.warnings);
|
|
163
166
|
if (seenNames.has(res.manifest.name)) {
|
|
164
167
|
warnings.push(`${p}: duplicate plugin name "${res.manifest.name}" — first entry wins, skipped`);
|
|
165
168
|
continue;
|
|
@@ -285,9 +288,15 @@ export async function exportVersion(name, sha, { exec = defaultExec, repoUrl, su
|
|
|
285
288
|
const scratch = await mkdtemp(join(tmpdir(), 'worca-cc-export-'));
|
|
286
289
|
const tarFile = join(scratch, 'export.tar');
|
|
287
290
|
try {
|
|
288
|
-
await gitDir(cache, ['archive', '--format=tar', '-o', tarFile, ...(sub ? [sha, '--', sub] : [sha])], exec);
|
|
291
|
+
await gitDir(cache, ['-c', 'core.autocrlf=false', 'archive', '--format=tar', '-o', tarFile, ...(sub ? [sha, '--', sub] : [sha])], exec);
|
|
289
292
|
const strip = sub ? ['--strip-components', String(sub.split('/').length)] : [];
|
|
290
|
-
|
|
293
|
+
// Windows GNU tar (shipped by Git for Windows) mishandles native paths two
|
|
294
|
+
// ways: a colon in the `-f` value is read as a `host:path` remote spec ("cannot
|
|
295
|
+
// connect to C:"), and backslash separators are not understood at all. So pass
|
|
296
|
+
// the archive as a RELATIVE name resolved against cwd, and give `-C` a
|
|
297
|
+
// forward-slashed path. On POSIX the replace is a no-op.
|
|
298
|
+
await exec('tar', ['-xf', 'export.tar', '-C', versionDir.replace(/\\/g, '/'), ...strip],
|
|
299
|
+
{ cwd: scratch });
|
|
291
300
|
} catch (err) {
|
|
292
301
|
rmSync(versionDir, { recursive: true, force: true });
|
|
293
302
|
throw err;
|
|
@@ -296,7 +305,10 @@ export async function exportVersion(name, sha, { exec = defaultExec, repoUrl, su
|
|
|
296
305
|
}
|
|
297
306
|
const warnings = [];
|
|
298
307
|
for (const rel of findEscapingSymlinks(versionDir)) {
|
|
299
|
-
|
|
308
|
+
// unlink, not rmSync: rmSync stats THROUGH the link, so a link to a
|
|
309
|
+
// directory throws ERR_FS_EISDIR and a dangling one (the common escaping
|
|
310
|
+
// case) reads as "already gone" and survives force:true (Node 23).
|
|
311
|
+
unlinkSync(join(versionDir, rel));
|
|
300
312
|
warnings.push(`removed symlink escaping the export dir: ${rel}`);
|
|
301
313
|
}
|
|
302
314
|
return { versionDir, warnings };
|
|
@@ -22,6 +22,10 @@ async function main() {
|
|
|
22
22
|
const stateDelta = {};
|
|
23
23
|
const ctx = {
|
|
24
24
|
apiVersion: msg.apiVersion ?? 1,
|
|
25
|
+
// Which profile this op runs against. config/state are ALREADY that
|
|
26
|
+
// profile's — the id is here for connectors that keep their own storage and
|
|
27
|
+
// must key it the same way (e.g. a per-profile CLI config dir).
|
|
28
|
+
profile: typeof msg.profile === 'string' && msg.profile ? msg.profile : 'default',
|
|
25
29
|
config: msg.config && typeof msg.config === 'object' ? msg.config : {},
|
|
26
30
|
state: {
|
|
27
31
|
get: async (k) => (k in stateDelta ? stateDelta[k] : (snapshot[k] ?? null)),
|
|
@@ -37,9 +41,11 @@ async function main() {
|
|
|
37
41
|
const source = mod.default(ctx);
|
|
38
42
|
const fn = source?.[msg.op];
|
|
39
43
|
if (typeof fn !== 'function') {
|
|
40
|
-
// Optional ops (e.g. capabilities) land here
|
|
41
|
-
//
|
|
42
|
-
|
|
44
|
+
// Optional ops (e.g. capabilities) land here. The kind is 'unimplemented',
|
|
45
|
+
// NOT 'plugin': an op the connector lacks and an implemented op that
|
|
46
|
+
// CRASHED must stay distinguishable — the capabilities probe defaults
|
|
47
|
+
// writeBack:true only for the former (Task 13 / sources.mjs).
|
|
48
|
+
throw Object.assign(new Error(`connector does not implement op "${msg.op}"`), { kind: 'unimplemented' });
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
// §7.1 signatures: getTask(id) and reportResult(id, r) are positional; every
|
package/src/core/plugin-shim.mjs
CHANGED
|
@@ -16,19 +16,23 @@ import { spawn } from 'node:child_process';
|
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
17
|
import { join, resolve } from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
+
import { envFlag } from './model-env.mjs';
|
|
19
20
|
import { WORCA_PLUGIN_API } from './plugin-api.mjs';
|
|
20
21
|
import { normalizeManifest, negotiatedApi } from './plugin-manifest.mjs';
|
|
21
22
|
import { readPluginsLock, pluginCurrentDir } from './plugins-lock.mjs';
|
|
22
|
-
import { readPluginConfig, readPluginState, writePluginState } from './plugin-config.mjs';
|
|
23
|
+
import { readPluginConfig, readPluginState, writePluginState, listProfileIds, DEFAULT_PROFILE } from './plugin-config.mjs';
|
|
23
24
|
|
|
24
25
|
const CHILD_PATH = fileURLToPath(new URL('./plugin-shim-child.mjs', import.meta.url));
|
|
25
26
|
|
|
26
|
-
/** Error kinds an op can surface (spec §11)
|
|
27
|
-
|
|
27
|
+
/** Error kinds an op can surface (spec §11) plus the host-synthesized
|
|
28
|
+
* 'unimplemented' (the connector lacks the op — distinct from an implemented
|
|
29
|
+
* op that CRASHED, because callers like the capabilities probe must default
|
|
30
|
+
* differently for the two); anything else normalizes to 'plugin'. */
|
|
31
|
+
const KINDS = new Set(['auth', 'rate-limit', 'network', 'plugin', 'timeout', 'protocol', 'unimplemented']);
|
|
28
32
|
|
|
29
33
|
export class PluginOpError extends Error {
|
|
30
34
|
/**
|
|
31
|
-
* @param {'auth'|'rate-limit'|'network'|'plugin'|'timeout'|'protocol'} kind
|
|
35
|
+
* @param {'auth'|'rate-limit'|'network'|'plugin'|'timeout'|'protocol'|'unimplemented'} kind
|
|
32
36
|
* @param {string} message
|
|
33
37
|
*/
|
|
34
38
|
constructor(kind, message) {
|
|
@@ -63,10 +67,9 @@ const MOCK_DEFAULTS = {
|
|
|
63
67
|
validateConfig: () => ({ ok: true }),
|
|
64
68
|
};
|
|
65
69
|
|
|
66
|
-
/** Same env-flag semantics as claude-runner.mjs#mockEnabled
|
|
70
|
+
/** Same env-flag semantics as claude-runner.mjs#mockEnabled — one shared rule. */
|
|
67
71
|
function mockMode() {
|
|
68
|
-
|
|
69
|
-
return !!v && v !== '0' && v.toLowerCase() !== 'false';
|
|
72
|
+
return envFlag('WORCA_MOCK', 'ORCH_MOCK');
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
async function mockCall(op, args) {
|
|
@@ -74,7 +77,7 @@ async function mockCall(op, args) {
|
|
|
74
77
|
if (entry === undefined) {
|
|
75
78
|
// Mirror the real child's answer for an unimplemented op — Task 13's
|
|
76
79
|
// capabilities tolerant-default keys on exactly this kind.
|
|
77
|
-
throw new PluginOpError('
|
|
80
|
+
throw new PluginOpError('unimplemented', `mock: no canned response for op "${op}"`);
|
|
78
81
|
}
|
|
79
82
|
try {
|
|
80
83
|
return await (typeof entry === 'function' ? entry(args) : entry);
|
|
@@ -112,40 +115,100 @@ function loadSource(plugin, sourceId) {
|
|
|
112
115
|
/** Child env: PATH + HOME ONLY (spec §7.2). Notably NOT WORCA_*, tokens, npm_*.
|
|
113
116
|
* Exported for the channel-worker supervisor (chat/channel-host.mjs), which
|
|
114
117
|
* spawns persistent children under the same rule. */
|
|
115
|
-
export function scrubbedEnv() {
|
|
118
|
+
export function scrubbedEnv(platform = process.platform) {
|
|
116
119
|
const env = {};
|
|
117
120
|
if (process.env.PATH) env.PATH = process.env.PATH;
|
|
118
121
|
if (process.env.HOME) env.HOME = process.env.HOME;
|
|
122
|
+
// Windows has no HOME and a child cannot even start without SYSTEMROOT (Node
|
|
123
|
+
// docs); the rest are the non-secret system vars node/npm need to resolve
|
|
124
|
+
// temp dirs, the shell, and the .cmd/.exe lookup. Still no WORCA_*, tokens
|
|
125
|
+
// or npm_* — the §7.2 rule is unchanged, only the platform baseline differs.
|
|
126
|
+
if (platform === 'win32') {
|
|
127
|
+
for (const k of WIN32_ENV_BASELINE) if (process.env[k]) env[k] = process.env[k];
|
|
128
|
+
}
|
|
119
129
|
return env;
|
|
120
130
|
}
|
|
121
131
|
|
|
132
|
+
/** Non-secret Windows system vars a child needs to run at all (spec §7.2 note). */
|
|
133
|
+
export const WIN32_ENV_BASELINE = Object.freeze([
|
|
134
|
+
'USERPROFILE', 'SYSTEMROOT', 'SYSTEMDRIVE', 'COMSPEC', 'PATHEXT', 'TEMP', 'TMP', 'APPDATA', 'LOCALAPPDATA',
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
/** The profile invariant, enforced in the shim itself so EVERY caller — the
|
|
138
|
+
* CLI's `worca plugin exec`, future workers — is safe by construction, not
|
|
139
|
+
* just the HTTP routes. Presence alone is not enough: a typo'd or deleted
|
|
140
|
+
* profile passes a presence check, silently runs against an EMPTY bucket
|
|
141
|
+
* (false "not configured" verdicts) and then persists state into a phantom
|
|
142
|
+
* bucket the DELETE route can never purge — so membership in the roster is
|
|
143
|
+
* checked here too. `allowLegacyDefault` is the ONE host-internal exception:
|
|
144
|
+
* write-back for a pipeline row that predates profiles targets the
|
|
145
|
+
* DEFAULT_PROFILE bucket, because that is where the pre-upgrade flat config
|
|
146
|
+
* migrated — i.e. the instance the task actually came from (sources.mjs). */
|
|
147
|
+
function assertProfileInvariant({ plugin, sourceId, source, profile, allowLegacyDefault }) {
|
|
148
|
+
if (source.multiProfile === true) {
|
|
149
|
+
if (!profile) {
|
|
150
|
+
throw new PluginOpError('plugin',
|
|
151
|
+
`task source "${plugin}/${sourceId}" has per-profile configuration — pass a profile (e.g. --profile <id>)`);
|
|
152
|
+
}
|
|
153
|
+
if (profile === DEFAULT_PROFILE) {
|
|
154
|
+
if (!allowLegacyDefault) {
|
|
155
|
+
throw new PluginOpError('plugin',
|
|
156
|
+
`"${DEFAULT_PROFILE}" is the reserved shared bucket, not a profile of "${plugin}/${sourceId}" — pass a profile from the roster`);
|
|
157
|
+
}
|
|
158
|
+
} else if (!listProfileIds(plugin).includes(profile)) {
|
|
159
|
+
throw new PluginOpError('plugin',
|
|
160
|
+
`plugin "${plugin}" has no profile "${profile}" — create it in Plugins settings (or POST /api/plugins/${plugin}/profiles) first`);
|
|
161
|
+
}
|
|
162
|
+
} else if (profile && profile !== DEFAULT_PROFILE) {
|
|
163
|
+
throw new PluginOpError('plugin', `task source "${plugin}/${sourceId}" does not use profiles`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
122
167
|
/**
|
|
123
168
|
* Run ONE connector op in an ephemeral child. Resolves with the op result;
|
|
124
169
|
* rejects with PluginOpError. `logger(level, msg)` is optional — connector
|
|
125
170
|
* ctx.log lines route there (default: console.error, since stdout is the UI's).
|
|
171
|
+
* `allowLegacyDefault` (host-internal, never surfaced to CLI/routes) lets the
|
|
172
|
+
* legacy write-back path name DEFAULT_PROFILE on a multiProfile source — see
|
|
173
|
+
* assertProfileInvariant.
|
|
126
174
|
* @returns {Promise<any>}
|
|
127
175
|
*/
|
|
128
|
-
export async function callSource({ plugin, sourceId, op, args = {}, timeoutMs = 30000, logger } = {}) {
|
|
176
|
+
export async function callSource({ plugin, sourceId, op, args = {}, profile, timeoutMs = 30000, logger, allowLegacyDefault = false } = {}) {
|
|
129
177
|
const log = typeof logger === 'function'
|
|
130
178
|
? logger
|
|
131
179
|
: (level, msg) => console.error(`[plugin:${plugin}] ${level}: ${msg}`);
|
|
180
|
+
// Which configuration of the source to run against (plugin-config.mjs
|
|
181
|
+
// profiles). Absent -> DEFAULT_PROFILE, which is what every single-profile
|
|
182
|
+
// source uses, so this parameter is invisible unless a plugin opts in.
|
|
183
|
+
const prof = profile || DEFAULT_PROFILE;
|
|
132
184
|
|
|
133
185
|
if (mockMode()) {
|
|
134
|
-
// Canned responses, no spawn, no plugin needed.
|
|
135
|
-
//
|
|
136
|
-
//
|
|
186
|
+
// Canned responses, no spawn, no plugin needed. The profile invariant still
|
|
187
|
+
// holds whenever the plugin IS installed (behavioral parity so mock runs
|
|
188
|
+
// catch missing/typo'd profiles too); an uninstalled plugin — the offline
|
|
189
|
+
// smoke's whole point — skips it. reportResult additionally records its
|
|
190
|
+
// args into the plugin state — mirroring the real-child stateDelta path —
|
|
191
|
+
// so the offline smoke (Task 19) can assert write-back ran.
|
|
192
|
+
let mockSource = null;
|
|
193
|
+
try { mockSource = loadSource(plugin, sourceId).source; } catch { mockSource = null; }
|
|
194
|
+
if (mockSource) assertProfileInvariant({ plugin, sourceId, source: mockSource, profile, allowLegacyDefault });
|
|
137
195
|
const r = await mockCall(op, args);
|
|
138
|
-
if (op === 'reportResult') writePluginState(plugin, { lastReport: JSON.stringify(args) });
|
|
196
|
+
if (op === 'reportResult') writePluginState(plugin, { lastReport: JSON.stringify(args) }, prof);
|
|
139
197
|
return r;
|
|
140
198
|
}
|
|
141
199
|
|
|
142
200
|
const { dir, source, apiVersion } = loadSource(plugin, sourceId);
|
|
201
|
+
assertProfileInvariant({ plugin, sourceId, source, profile, allowLegacyDefault });
|
|
143
202
|
const payload = JSON.stringify({
|
|
144
203
|
apiVersion,
|
|
145
204
|
module: resolve(dir, source.module), // './'-relative, '..'-free (normalizeManifest)
|
|
146
205
|
op,
|
|
147
|
-
config
|
|
148
|
-
|
|
206
|
+
// The connector sees ONE profile's config/state and is told which — a
|
|
207
|
+
// connector that keeps its own storage (jira-source hangs a $JTR_CONFIG_DIR
|
|
208
|
+
// off it) needs the id, not just the values.
|
|
209
|
+
profile: prof,
|
|
210
|
+
config: readPluginConfig(plugin, source.configSchema, prof),
|
|
211
|
+
state: readPluginState(plugin, prof),
|
|
149
212
|
args,
|
|
150
213
|
});
|
|
151
214
|
|
|
@@ -191,7 +254,7 @@ export async function callSource({ plugin, sourceId, op, args = {}, timeoutMs =
|
|
|
191
254
|
throw new PluginOpError(frame.error?.kind || 'plugin', frame.error?.message || `plugin "${plugin}" op "${op}" failed`);
|
|
192
255
|
}
|
|
193
256
|
if (frame.stateDelta && typeof frame.stateDelta === 'object' && Object.keys(frame.stateDelta).length) {
|
|
194
|
-
writePluginState(plugin, frame.stateDelta); // host-side persist; child never touches the store
|
|
257
|
+
writePluginState(plugin, frame.stateDelta, prof); // host-side persist; child never touches the store
|
|
195
258
|
}
|
|
196
259
|
return frame.result;
|
|
197
260
|
}
|