@runfusion/fusion 0.26.0 → 0.27.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/dist/bin.js +11036 -1992
- package/dist/client/assets/AgentDetailView-B7QRcHJH.css +1 -0
- package/dist/client/assets/AgentDetailView-DwLmRXTY.js +18 -0
- package/dist/client/assets/{AgentsView-D6Zi5zfP.js → AgentsView-D-N6aA0P.js} +12 -7
- package/dist/client/assets/ChatView-DnCdKu8Z.js +1 -0
- package/dist/client/assets/{DevServerView--_WBvIDQ.js → DevServerView-BiA1nYtt.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-xedtR-Rd.js → DirectoryPicker-DvBviDG6.js} +1 -1
- package/dist/client/assets/{DocumentsView-Bg2oaZks.js → DocumentsView-BWXOxpuq.js} +1 -1
- package/dist/client/assets/{EvalsView-B3uOCXfr.js → EvalsView-CJFbtL7i.js} +1 -1
- package/dist/client/assets/{ExperimentalAgentOnboardingModal-Bx6yXVS5.js → ExperimentalAgentOnboardingModal-DuGIPd0B.js} +1 -1
- package/dist/client/assets/InsightsView-BBpRiolN.js +11 -0
- package/dist/client/assets/{MemoryView-xcN_eouf.js → MemoryView-48LuNkKk.js} +2 -2
- package/dist/client/assets/NodesView-CGQWSNZM.js +14 -0
- package/dist/client/assets/{PiExtensionsManager-Cc8aAZXg.js → PiExtensionsManager-i-7UL2oh.js} +2 -2
- package/dist/client/assets/PluginManager-DoSAykD6.js +1 -0
- package/dist/client/assets/{ResearchView-CERNf7sJ.js → ResearchView-XZuRtOxE.js} +1 -1
- package/dist/client/assets/{SettingsModal-Cis-4Lot.css → SettingsModal-Ci0_sqbU.css} +1 -1
- package/dist/client/assets/{SettingsModal-B1r0yASu.js → SettingsModal-CmeF8CN4.js} +1 -1
- package/dist/client/assets/SettingsModal-DBcjf9Bu.js +31 -0
- package/dist/client/assets/SettingsModal-DWKgRxBA.css +1 -0
- package/dist/client/assets/{SetupWizardModal-D1q548_L.js → SetupWizardModal-CgtvpMX9.js} +1 -1
- package/dist/client/assets/{SkillsView-ClLM6u6p.js → SkillsView-DErYRumF.js} +1 -1
- package/dist/client/assets/{StashRecoveryView-ze0pEZ5U.js → StashRecoveryView-QJrNS4Vg.js} +1 -1
- package/dist/client/assets/{TodoView-CTmIfy2M.js → TodoView-BD9NRwq0.js} +2 -2
- package/dist/client/assets/{dashboard-view-CyWN-d02.js → dashboard-view-BWGH_fAq.js} +1 -1
- package/dist/client/assets/dashboard-view-BoTzlP8b.css +1 -0
- package/dist/client/assets/dashboard-view-Ws9_ZnKu.js +21 -0
- package/dist/client/assets/{folder-open-BZuKESeq.js → folder-open-CHSlllzf.js} +1 -1
- package/dist/client/assets/index-DCovGm5b.css +1 -0
- package/dist/client/assets/index-bEwSVl7B.js +692 -0
- package/dist/client/assets/{star-D75YKEq-.js → star-BgVwWAPz.js} +1 -1
- package/dist/client/assets/{upload-BYYTgWFj.js → upload-CAzycxr9.js} +1 -1
- package/dist/client/assets/{users-RS90Aii3.js → users-CZnxCCCJ.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/package.json +1 -1
- package/dist/droid-cli/src/__tests__/index.test.ts +228 -0
- package/dist/extension.js +5517 -1193
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/pi-claude-cli/src/__tests__/provider.test.ts +36 -22
- package/dist/pi-claude-cli/src/provider.ts +7 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/manifest.json +19 -1
- package/dist/plugins/fusion-plugin-cli-printing-press/package.json +20 -2
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/TestRunnerPanel.test.tsx +99 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/config-flow.test.ts +91 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-view.test.tsx +40 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/dashboard-views.test.ts +46 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/draft-store.test.ts +50 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/exec-mock.ts +80 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/fixtures.test.ts +40 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/fixtures/registry.ts +82 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/generator.test.ts +54 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manage-view.test.tsx +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/manifest.test.ts +21 -5
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/registration.test.ts +29 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/run-routes.test.ts +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runner.test.ts +55 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/runtime-availability.test.ts +61 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/validation.test.ts +30 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/wizard-routes.test.ts +61 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/__tests__/workflow-integration.test.ts +19 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.css +43 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/dashboard-view.tsx +49 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/generator.ts +95 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/redact.ts +9 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/runner.ts +79 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/generation/types.ts +31 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/index.ts +46 -2
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/EditDraftModal.tsx +75 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage/useDrafts.ts +73 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.css +79 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/manage-view.tsx +122 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/routes/wizard-routes.ts +272 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.css +70 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/TestRunnerPanel.tsx +98 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/run/useRunGeneratedCli.ts +37 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/__tests__/executor-runtime-env.test.ts +191 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/runtime/executor-runtime-env.ts +75 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/storage/draft-store.ts +85 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/cli-press-store.test.ts +128 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/credentials.test.ts +62 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-store.ts +427 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/cli-press-types.ts +110 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/store/credentials.ts +95 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/steps.tsx +55 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/types.ts +33 -0
- package/dist/plugins/fusion-plugin-cli-printing-press/src/wizard/validation.ts +63 -0
- package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
- package/dist/plugins/fusion-plugin-reports/manifest.json +10 -0
- package/dist/plugins/fusion-plugin-reports/package.json +18 -2
- package/dist/plugins/fusion-plugin-reports/src/__tests__/approval.test.ts +164 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +14 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/routes-approval.test.ts +109 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/scaffold.test.ts +60 -0
- package/dist/plugins/fusion-plugin-reports/src/__tests__/share-blocks.test.ts +83 -0
- package/dist/plugins/fusion-plugin-reports/src/aggregation.ts +23 -0
- package/dist/plugins/fusion-plugin-reports/src/approval.ts +97 -0
- package/dist/plugins/fusion-plugin-reports/src/cadence.ts +23 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.css +82 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/ReportsView.tsx +24 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportComparisonDrawer.test.tsx +12 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportDetailPanel.test.tsx +12 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportFiltersBar.test.tsx +14 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/ReportsView.test.tsx +27 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/api.test.ts +19 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReportSectionDiff.test.ts +11 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/__tests__/useReports.test.ts +13 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/api.ts +85 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.css +59 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportApprovalPanel.tsx +58 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportComparisonDrawer.tsx +21 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportDetailPanel.tsx +29 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportEmptyState.tsx +3 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportFiltersBar.tsx +19 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ReportListItem.tsx +8 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.css +29 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/ShareBlocksPanel.tsx +43 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ReportApprovalPanel.test.tsx +38 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/components/__tests__/ShareBlocksPanel.test.tsx +24 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/test-setup.ts +18 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/types.ts +22 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportPreview.ts +44 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReportSectionDiff.ts +59 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useReports.ts +71 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard/useViewportMode.ts +13 -0
- package/dist/plugins/fusion-plugin-reports/src/dashboard-view.tsx +6 -0
- package/dist/plugins/fusion-plugin-reports/src/index.ts +48 -2
- package/dist/plugins/fusion-plugin-reports/src/pipeline.ts +58 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/escape.test.ts +20 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/html-template.test.ts +110 -0
- package/dist/plugins/fusion-plugin-reports/src/render/__tests__/standalone-html.test.ts +66 -0
- package/dist/plugins/fusion-plugin-reports/src/render/escape.ts +12 -0
- package/dist/plugins/fusion-plugin-reports/src/render/html-styles.ts +40 -0
- package/dist/plugins/fusion-plugin-reports/src/render/html-template.ts +137 -0
- package/dist/plugins/fusion-plugin-reports/src/render/index.ts +4 -0
- package/dist/plugins/fusion-plugin-reports/src/render/standalone-html.ts +75 -0
- package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +31 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/__tests__/report-export-routes.test.ts +104 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-approval-routes.ts +98 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-export-routes.ts +77 -0
- package/dist/plugins/fusion-plugin-reports/src/routes/report-list-routes.ts +72 -0
- package/dist/plugins/fusion-plugin-reports/src/runs-store.ts +69 -0
- package/dist/plugins/fusion-plugin-reports/src/share-blocks.ts +82 -0
- package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +51 -2
- package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +6 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.js +1528 -29391
- package/dist/plugins/fusion-plugin-roadmap/manifest.json +1 -1
- package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
- package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
- package/package.json +1 -1
- package/skill/fusion/references/engine-tools.md +1 -1
- package/skill/fusion/references/extension-tools.md +3 -3
- package/skill/fusion/references/fusion-capabilities.md +1 -1
- package/dist/client/assets/AgentDetailView-BwJaLqZh.css +0 -1
- package/dist/client/assets/AgentDetailView-Cv-vgOj3.js +0 -18
- package/dist/client/assets/ChatView-CAHjY9uO.js +0 -1
- package/dist/client/assets/InsightsView-Q1zvtF4F.js +0 -11
- package/dist/client/assets/NodesView-RxXg58_Q.js +0 -14
- package/dist/client/assets/PluginManager-BEkyBajl.js +0 -1
- package/dist/client/assets/SettingsModal-BLsac7CJ.js +0 -31
- package/dist/client/assets/SettingsModal-BNSrO1M9.css +0 -1
- package/dist/client/assets/dashboard-view-4xAN3yO5.js +0 -21
- package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
- package/dist/client/assets/index-Bdw6llW6.js +0 -692
- package/dist/client/assets/index-CZGlyJuS.css +0 -1
- package/dist/plugins/fusion-plugin-roadmap/bundled.css +0 -1093
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Database } from "@fusion/core";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { createCliPressStore } from "../../store/cli-press-store.js";
|
|
7
|
+
import { encodeCredentialValue } from "../../store/credentials.js";
|
|
8
|
+
import { buildExecutorRuntimeEnv } from "../executor-runtime-env.js";
|
|
9
|
+
|
|
10
|
+
function createHarness() {
|
|
11
|
+
const rootDir = mkdtempSync(join(tmpdir(), "cli-press-runtime-env-"));
|
|
12
|
+
const db = new Database(join(rootDir, ".fusion"), { inMemory: true });
|
|
13
|
+
db.init();
|
|
14
|
+
const store = createCliPressStore(db);
|
|
15
|
+
const warnings: string[] = [];
|
|
16
|
+
|
|
17
|
+
const ctx = {
|
|
18
|
+
pluginId: "fusion-plugin-cli-printing-press",
|
|
19
|
+
taskStore: undefined,
|
|
20
|
+
settings: {},
|
|
21
|
+
logger: {
|
|
22
|
+
log: () => {},
|
|
23
|
+
info: () => {},
|
|
24
|
+
warn: (msg: string) => warnings.push(msg),
|
|
25
|
+
error: () => {},
|
|
26
|
+
debug: () => {},
|
|
27
|
+
},
|
|
28
|
+
emitEvent: () => {},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const cleanup = () => {
|
|
32
|
+
db.close();
|
|
33
|
+
rmSync(rootDir, { recursive: true, force: true });
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
return { rootDir, db, store, warnings, ctx, cleanup };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("buildExecutorRuntimeEnv", () => {
|
|
40
|
+
it("returns empty env/path when no generated services are present", () => {
|
|
41
|
+
const h = createHarness();
|
|
42
|
+
try {
|
|
43
|
+
const result = buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never);
|
|
44
|
+
expect(result.pathPrepend).toEqual([]);
|
|
45
|
+
expect(result.env).toEqual({});
|
|
46
|
+
} finally {
|
|
47
|
+
h.cleanup();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("adds executable artifact directory and env_var credential", () => {
|
|
52
|
+
const h = createHarness();
|
|
53
|
+
try {
|
|
54
|
+
const service = h.store.createService({
|
|
55
|
+
slug: "alpha",
|
|
56
|
+
displayName: "Alpha",
|
|
57
|
+
baseUrl: "https://example.com",
|
|
58
|
+
sourceKind: "manual",
|
|
59
|
+
});
|
|
60
|
+
const spec = h.store.createSpec({
|
|
61
|
+
serviceId: service.id,
|
|
62
|
+
name: "alpha-cli",
|
|
63
|
+
version: "0.1.0",
|
|
64
|
+
generatorVersion: "1.0.0",
|
|
65
|
+
specJson: "{}",
|
|
66
|
+
status: "generated",
|
|
67
|
+
generatedAt: new Date().toISOString(),
|
|
68
|
+
});
|
|
69
|
+
const relativePath = `plugins/cli-printing-press/artifacts/${service.id}/${spec.id}/alpha`;
|
|
70
|
+
const absolutePath = join(h.rootDir, ".fusion", relativePath);
|
|
71
|
+
mkdirSync(join(absolutePath, ".."), { recursive: true });
|
|
72
|
+
writeFileSync(absolutePath, "#!/bin/sh\necho alpha\n");
|
|
73
|
+
|
|
74
|
+
h.store.createArtifact({ cliSpecId: spec.id, kind: "script", path: relativePath, executable: true });
|
|
75
|
+
h.store.createCredential({
|
|
76
|
+
serviceId: service.id,
|
|
77
|
+
name: "api",
|
|
78
|
+
kind: "env_var",
|
|
79
|
+
placement: { kind: "env_var", envVar: "ALPHA_TOKEN" },
|
|
80
|
+
value: encodeCredentialValue("secret-alpha"),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never);
|
|
84
|
+
expect(result.pathPrepend).toEqual([join(h.rootDir, ".fusion", "plugins/cli-printing-press/artifacts", service.id, spec.id)]);
|
|
85
|
+
expect(result.env).toEqual({ ALPHA_TOKEN: "secret-alpha" });
|
|
86
|
+
} finally {
|
|
87
|
+
h.cleanup();
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("deduplicates path entries across services", () => {
|
|
92
|
+
const h = createHarness();
|
|
93
|
+
try {
|
|
94
|
+
const sharedDir = `plugins/cli-printing-press/artifacts/shared/bin`;
|
|
95
|
+
const sharedExec1 = `${sharedDir}/one`;
|
|
96
|
+
const sharedExec2 = `${sharedDir}/two`;
|
|
97
|
+
mkdirSync(join(h.rootDir, ".fusion", sharedDir), { recursive: true });
|
|
98
|
+
writeFileSync(join(h.rootDir, ".fusion", sharedExec1), "1");
|
|
99
|
+
writeFileSync(join(h.rootDir, ".fusion", sharedExec2), "2");
|
|
100
|
+
|
|
101
|
+
for (const slug of ["one", "two"]) {
|
|
102
|
+
const service = h.store.createService({ slug, displayName: slug, baseUrl: "https://example.com", sourceKind: "manual" });
|
|
103
|
+
const spec = h.store.createSpec({
|
|
104
|
+
serviceId: service.id,
|
|
105
|
+
name: `${slug}-cli`,
|
|
106
|
+
version: "0.1.0",
|
|
107
|
+
generatorVersion: "1.0.0",
|
|
108
|
+
specJson: "{}",
|
|
109
|
+
status: "generated",
|
|
110
|
+
generatedAt: new Date().toISOString(),
|
|
111
|
+
});
|
|
112
|
+
h.store.createArtifact({
|
|
113
|
+
cliSpecId: spec.id,
|
|
114
|
+
kind: "script",
|
|
115
|
+
path: slug === "one" ? sharedExec1 : sharedExec2,
|
|
116
|
+
executable: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result = buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never);
|
|
121
|
+
expect(result.pathPrepend).toEqual([join(h.rootDir, ".fusion", sharedDir)]);
|
|
122
|
+
} finally {
|
|
123
|
+
h.cleanup();
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("skips missing artifacts and logs warning", () => {
|
|
128
|
+
const h = createHarness();
|
|
129
|
+
try {
|
|
130
|
+
const service = h.store.createService({ slug: "missing", displayName: "Missing", baseUrl: "https://example.com", sourceKind: "manual" });
|
|
131
|
+
const spec = h.store.createSpec({
|
|
132
|
+
serviceId: service.id,
|
|
133
|
+
name: "missing-cli",
|
|
134
|
+
version: "0.1.0",
|
|
135
|
+
generatorVersion: "1.0.0",
|
|
136
|
+
specJson: "{}",
|
|
137
|
+
status: "generated",
|
|
138
|
+
generatedAt: new Date().toISOString(),
|
|
139
|
+
});
|
|
140
|
+
h.store.createArtifact({
|
|
141
|
+
cliSpecId: spec.id,
|
|
142
|
+
kind: "script",
|
|
143
|
+
path: `plugins/cli-printing-press/artifacts/${service.id}/${spec.id}/missing`,
|
|
144
|
+
executable: true,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const result = buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never);
|
|
148
|
+
expect(result.pathPrepend).toEqual([]);
|
|
149
|
+
expect(h.warnings.length).toBe(1);
|
|
150
|
+
expect(h.warnings[0]).toContain("Skipping missing artifact");
|
|
151
|
+
} finally {
|
|
152
|
+
h.cleanup();
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("rejects oauth credentials defensively", () => {
|
|
157
|
+
const h = createHarness();
|
|
158
|
+
try {
|
|
159
|
+
const service = h.store.createService({ slug: "oauth", displayName: "OAuth", baseUrl: "https://example.com", sourceKind: "manual" });
|
|
160
|
+
const encoded = JSON.stringify(encodeCredentialValue("token"));
|
|
161
|
+
const placement = JSON.stringify({ kind: "oauth", provider: "x" });
|
|
162
|
+
h.db.prepare("INSERT INTO cli_press_credentials (id, serviceId, name, kind, value, placement, createdAt, updatedAt) VALUES (?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))")
|
|
163
|
+
.run("cred_oauth", service.id, "oauth", "oauth", encoded, placement);
|
|
164
|
+
|
|
165
|
+
expect(() =>
|
|
166
|
+
buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never),
|
|
167
|
+
).toThrow(/OAuth credentials are not supported/);
|
|
168
|
+
} finally {
|
|
169
|
+
h.cleanup();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("ignores non env_var credentials", () => {
|
|
174
|
+
const h = createHarness();
|
|
175
|
+
try {
|
|
176
|
+
const service = h.store.createService({ slug: "beta", displayName: "Beta", baseUrl: "https://example.com", sourceKind: "manual" });
|
|
177
|
+
h.store.createCredential({
|
|
178
|
+
serviceId: service.id,
|
|
179
|
+
name: "header",
|
|
180
|
+
kind: "header",
|
|
181
|
+
placement: { kind: "header", header: "X-Token" },
|
|
182
|
+
value: encodeCredentialValue("header-token"),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const result = buildExecutorRuntimeEnv(h.store, { taskId: "FN-1", worktreePath: h.rootDir, rootDir: h.rootDir }, h.ctx as never);
|
|
186
|
+
expect(result.env).toEqual({});
|
|
187
|
+
} finally {
|
|
188
|
+
h.cleanup();
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import type { ExecutorRuntimeEnvContribution, ExecutorRuntimeTaskContext, PluginContext } from "@fusion/plugin-sdk";
|
|
4
|
+
import type { createCliPressStore } from "../store/cli-press-store.js";
|
|
5
|
+
import { decodeCredentialValue } from "../store/credentials.js";
|
|
6
|
+
|
|
7
|
+
type CliPressStore = ReturnType<typeof createCliPressStore>;
|
|
8
|
+
|
|
9
|
+
function toEpoch(value?: string): number {
|
|
10
|
+
if (!value) return 0;
|
|
11
|
+
const parsed = Date.parse(value);
|
|
12
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildExecutorRuntimeEnv(
|
|
16
|
+
store: CliPressStore,
|
|
17
|
+
taskCtx: ExecutorRuntimeTaskContext,
|
|
18
|
+
ctx: PluginContext,
|
|
19
|
+
): ExecutorRuntimeEnvContribution {
|
|
20
|
+
const pathDirs: string[] = [];
|
|
21
|
+
const env: Record<string, string> = {};
|
|
22
|
+
|
|
23
|
+
for (const service of store.listServices()) {
|
|
24
|
+
const specs = store
|
|
25
|
+
.listSpecs(service.id)
|
|
26
|
+
.filter((spec) => spec.status === "generated")
|
|
27
|
+
.sort((a, b) => toEpoch(b.generatedAt ?? b.updatedAt) - toEpoch(a.generatedAt ?? a.updatedAt));
|
|
28
|
+
|
|
29
|
+
const selectedSpec = specs.find((spec) => {
|
|
30
|
+
const artifacts = store.listArtifacts(spec.id);
|
|
31
|
+
return artifacts.some((artifact) => artifact.executable);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (selectedSpec) {
|
|
35
|
+
const executableArtifacts = store.listArtifacts(selectedSpec.id).filter((artifact) => artifact.executable);
|
|
36
|
+
for (const artifact of executableArtifacts) {
|
|
37
|
+
const absoluteArtifactPath = isAbsolute(artifact.path)
|
|
38
|
+
? artifact.path
|
|
39
|
+
: join(taskCtx.rootDir, ".fusion", artifact.path);
|
|
40
|
+
if (!existsSync(absoluteArtifactPath)) {
|
|
41
|
+
ctx.logger.warn(
|
|
42
|
+
`[executorRuntimeEnv] Skipping missing artifact for service ${service.slug}: ${absoluteArtifactPath}`,
|
|
43
|
+
);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
pathDirs.push(dirname(absoluteArtifactPath));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const credential of store.listCredentials(service.id)) {
|
|
51
|
+
const credentialKind = (credential as { kind: string }).kind;
|
|
52
|
+
if (credentialKind === "oauth" || credentialKind === "oauth2") {
|
|
53
|
+
throw new Error(`OAuth credentials are not supported for service ${service.slug}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (credential.kind !== "env_var") {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (credential.placement.kind !== "env_var") {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Credential placement mismatch for ${credential.name}: expected env_var placement, got ${credential.placement.kind}`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
env[credential.placement.envVar] = decodeCredentialValue(credential.value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
pathPrepend: Array.from(new Set(pathDirs)),
|
|
72
|
+
env,
|
|
73
|
+
description: "cli-printing-press generated CLIs",
|
|
74
|
+
};
|
|
75
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Interim storage — replaced by FN-3766's canonical schema. Do not extend without updating that ticket. */
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import type { ServiceDraft } from "../wizard/types.js";
|
|
6
|
+
|
|
7
|
+
export class NotFoundError extends Error {
|
|
8
|
+
constructor(id: string) {
|
|
9
|
+
super(`Draft not found: ${id}`);
|
|
10
|
+
this.name = "NotFoundError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function mergeDraft(existing: ServiceDraft, patch: Partial<ServiceDraft>): ServiceDraft {
|
|
15
|
+
const mergedCredential = patch.credential && typeof patch.credential === "object"
|
|
16
|
+
? { ...existing.credential, ...patch.credential }
|
|
17
|
+
: existing.credential;
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
...existing,
|
|
21
|
+
...patch,
|
|
22
|
+
credential: mergedCredential,
|
|
23
|
+
endpoints: patch.endpoints ?? existing.endpoints,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getArtifactDir(id: string, projectRoot: string): string {
|
|
28
|
+
return join(projectRoot, ".fusion", "plugins", "cli-printing-press", "generated", id);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createDraftStore({ rootDir }: { rootDir: string }) {
|
|
32
|
+
const draftsDir = join(rootDir, ".fusion", "plugins", "cli-printing-press", "drafts");
|
|
33
|
+
|
|
34
|
+
async function ensureDir() { await mkdir(draftsDir, { recursive: true }); }
|
|
35
|
+
|
|
36
|
+
function nextUpdatedAt(previous?: string): string {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
const previousTime = previous ? Date.parse(previous) : Number.NaN;
|
|
39
|
+
if (Number.isFinite(previousTime) && now <= previousTime) {
|
|
40
|
+
return new Date(previousTime + 1).toISOString();
|
|
41
|
+
}
|
|
42
|
+
return new Date(now).toISOString();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function writeAtomic(path: string, draft: ServiceDraft): Promise<void> {
|
|
46
|
+
const tempPath = `${path}.tmp-${randomUUID()}`;
|
|
47
|
+
await writeFile(tempPath, JSON.stringify(draft, null, 2), "utf8");
|
|
48
|
+
await rename(tempPath, path);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
async create(input: ServiceDraft) {
|
|
53
|
+
await ensureDir();
|
|
54
|
+
const now = nextUpdatedAt();
|
|
55
|
+
const draft: ServiceDraft = { ...input, id: input.id || randomUUID(), createdAt: input.createdAt || now, updatedAt: now };
|
|
56
|
+
await writeAtomic(join(draftsDir, `${draft.id}.json`), draft);
|
|
57
|
+
return draft;
|
|
58
|
+
},
|
|
59
|
+
async list() {
|
|
60
|
+
await ensureDir();
|
|
61
|
+
const files = await readdir(draftsDir);
|
|
62
|
+
const entries = await Promise.all(files.filter((file) => file.endsWith(".json")).map(async (file) => JSON.parse(await readFile(join(draftsDir, file), "utf8")) as ServiceDraft));
|
|
63
|
+
return entries.map(({ id, name, slug, updatedAt }) => ({ id, name, slug, updatedAt }));
|
|
64
|
+
},
|
|
65
|
+
async get(id: string) {
|
|
66
|
+
try { return JSON.parse(await readFile(join(draftsDir, `${id}.json`), "utf8")) as ServiceDraft; } catch { return null; }
|
|
67
|
+
},
|
|
68
|
+
async update(id: string, patch: Partial<ServiceDraft>) {
|
|
69
|
+
await ensureDir();
|
|
70
|
+
const current = await this.get(id);
|
|
71
|
+
if (!current) throw new NotFoundError(id);
|
|
72
|
+
const updated: ServiceDraft = {
|
|
73
|
+
...mergeDraft(current, patch),
|
|
74
|
+
id: current.id,
|
|
75
|
+
createdAt: current.createdAt,
|
|
76
|
+
updatedAt: nextUpdatedAt(current.updatedAt),
|
|
77
|
+
};
|
|
78
|
+
await writeAtomic(join(draftsDir, `${id}.json`), updated);
|
|
79
|
+
return updated;
|
|
80
|
+
},
|
|
81
|
+
async delete(id: string) {
|
|
82
|
+
await rm(join(draftsDir, `${id}.json`), { force: true });
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
package/dist/plugins/fusion-plugin-cli-printing-press/src/store/__tests__/cli-press-store.test.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { mkdtempSync } from "node:fs";
|
|
2
|
+
import { rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { Database } from "@fusion/core";
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
7
|
+
import { createCliPressStore, ensureCliPressSchema } from "../cli-press-store.js";
|
|
8
|
+
import { encodeCredentialValue } from "../credentials.js";
|
|
9
|
+
|
|
10
|
+
describe("cli-press-store", () => {
|
|
11
|
+
let rootDir: string;
|
|
12
|
+
let db: Database;
|
|
13
|
+
let store: ReturnType<typeof createCliPressStore>;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
rootDir = mkdtempSync(join(tmpdir(), "cli-press-store-"));
|
|
17
|
+
db = new Database(join(rootDir, ".fusion"), { inMemory: true });
|
|
18
|
+
db.init();
|
|
19
|
+
ensureCliPressSchema(db);
|
|
20
|
+
ensureCliPressSchema(db);
|
|
21
|
+
store = createCliPressStore(db);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
db.close();
|
|
26
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("creates schema idempotently and runs full CRUD", () => {
|
|
30
|
+
const service = store.createService({
|
|
31
|
+
slug: "demo",
|
|
32
|
+
displayName: "Demo",
|
|
33
|
+
description: "Demo service",
|
|
34
|
+
baseUrl: "https://example.com",
|
|
35
|
+
sourceKind: "manual",
|
|
36
|
+
sourceRef: "wizard",
|
|
37
|
+
});
|
|
38
|
+
expect(service.id).toMatch(/^svc_/);
|
|
39
|
+
|
|
40
|
+
const updatedService = store.updateService(service.id, { displayName: "Demo Updated" });
|
|
41
|
+
expect(updatedService.displayName).toBe("Demo Updated");
|
|
42
|
+
|
|
43
|
+
const spec = store.createSpec({
|
|
44
|
+
serviceId: service.id,
|
|
45
|
+
name: "demo-cli",
|
|
46
|
+
version: "0.1.0",
|
|
47
|
+
generatorVersion: "1.0.0",
|
|
48
|
+
specJson: JSON.stringify({ hello: "world" }),
|
|
49
|
+
status: "draft",
|
|
50
|
+
generatedAt: undefined,
|
|
51
|
+
lastGenerationError: undefined,
|
|
52
|
+
});
|
|
53
|
+
expect(store.getSpec(spec.id)?.specJson).toBe(JSON.stringify({ hello: "world" }));
|
|
54
|
+
|
|
55
|
+
const updatedSpec = store.updateSpec(spec.id, { status: "generated" });
|
|
56
|
+
expect(updatedSpec.status).toBe("generated");
|
|
57
|
+
|
|
58
|
+
const artifact = store.createArtifact({
|
|
59
|
+
cliSpecId: spec.id,
|
|
60
|
+
kind: "script",
|
|
61
|
+
path: "plugins/cli-printing-press/artifacts/demo.sh",
|
|
62
|
+
executable: true,
|
|
63
|
+
checksum: "abc",
|
|
64
|
+
sizeBytes: 42,
|
|
65
|
+
});
|
|
66
|
+
expect(artifact.id).toMatch(/^art_/);
|
|
67
|
+
|
|
68
|
+
const cred = store.createCredential({
|
|
69
|
+
serviceId: service.id,
|
|
70
|
+
name: "api",
|
|
71
|
+
kind: "api_key",
|
|
72
|
+
value: encodeCredentialValue("secret"),
|
|
73
|
+
placement: { kind: "api_key", header: "X-API-Key" },
|
|
74
|
+
});
|
|
75
|
+
expect(cred.id).toMatch(/^cred_/);
|
|
76
|
+
|
|
77
|
+
const setting = store.setSetting({
|
|
78
|
+
serviceId: service.id,
|
|
79
|
+
key: "region",
|
|
80
|
+
value: "us-east-1",
|
|
81
|
+
scope: "runtime",
|
|
82
|
+
});
|
|
83
|
+
expect(setting.id).toMatch(/^set_/);
|
|
84
|
+
|
|
85
|
+
expect(store.listServices()).toHaveLength(1);
|
|
86
|
+
expect(store.listSpecs(service.id)).toHaveLength(1);
|
|
87
|
+
expect(store.listArtifacts(spec.id)).toHaveLength(1);
|
|
88
|
+
expect(store.listCredentials(service.id)).toHaveLength(1);
|
|
89
|
+
expect(store.listSettings(service.id)).toHaveLength(1);
|
|
90
|
+
|
|
91
|
+
store.deleteService(service.id);
|
|
92
|
+
expect(store.listServices()).toHaveLength(0);
|
|
93
|
+
expect(store.listSpecs(service.id)).toHaveLength(0);
|
|
94
|
+
expect(store.listCredentials(service.id)).toHaveLength(0);
|
|
95
|
+
expect(store.listSettings(service.id)).toHaveLength(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("rejects oauth and invalid placement", () => {
|
|
99
|
+
const service = store.createService({
|
|
100
|
+
slug: "oauth-demo",
|
|
101
|
+
displayName: "OAuth Demo",
|
|
102
|
+
description: "",
|
|
103
|
+
baseUrl: "https://example.com",
|
|
104
|
+
sourceKind: "manual",
|
|
105
|
+
sourceRef: "wizard",
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(() =>
|
|
109
|
+
store.createCredential({
|
|
110
|
+
serviceId: service.id,
|
|
111
|
+
name: "bad",
|
|
112
|
+
kind: "oauth" as never,
|
|
113
|
+
value: encodeCredentialValue("x"),
|
|
114
|
+
placement: { kind: "header", header: "Authorization" },
|
|
115
|
+
}),
|
|
116
|
+
).toThrow("not supported");
|
|
117
|
+
|
|
118
|
+
expect(() =>
|
|
119
|
+
store.createCredential({
|
|
120
|
+
serviceId: service.id,
|
|
121
|
+
name: "bad2",
|
|
122
|
+
kind: "api_key",
|
|
123
|
+
value: encodeCredentialValue("x"),
|
|
124
|
+
placement: { kind: "api_key", header: "X", queryParam: "token" },
|
|
125
|
+
}),
|
|
126
|
+
).toThrow("Invalid credential placement");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { Credential } from "../cli-press-types.js";
|
|
3
|
+
import { applyCredentialToRequest, decodeCredentialValue, encodeCredentialValue } from "../credentials.js";
|
|
4
|
+
|
|
5
|
+
function baseCredential(partial: Partial<Credential>): Credential {
|
|
6
|
+
return {
|
|
7
|
+
id: "cred_1",
|
|
8
|
+
serviceId: "svc_1",
|
|
9
|
+
name: "cred",
|
|
10
|
+
kind: "header",
|
|
11
|
+
value: encodeCredentialValue("secret"),
|
|
12
|
+
placement: { kind: "header", header: "X-Custom-Token" },
|
|
13
|
+
createdAt: new Date().toISOString(),
|
|
14
|
+
updatedAt: new Date().toISOString(),
|
|
15
|
+
...partial,
|
|
16
|
+
} as Credential;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("credentials", () => {
|
|
20
|
+
it("encodes and decodes", () => {
|
|
21
|
+
const encoded = encodeCredentialValue("alice:s3cret");
|
|
22
|
+
expect(encoded.encoding).toBe("base64");
|
|
23
|
+
expect(decodeCredentialValue(encoded)).toBe("alice:s3cret");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("applies all credential kinds", () => {
|
|
27
|
+
const request = { headers: {}, query: {}, env: {} };
|
|
28
|
+
|
|
29
|
+
applyCredentialToRequest(baseCredential({ kind: "header", placement: { kind: "header", header: "X-Custom-Token" } }), request);
|
|
30
|
+
applyCredentialToRequest(baseCredential({ kind: "query_param", placement: { kind: "query_param", queryParam: "api_token" } }), request);
|
|
31
|
+
applyCredentialToRequest(baseCredential({ kind: "env_var", placement: { kind: "env_var", envVar: "GITHUB_TOKEN" } }), request);
|
|
32
|
+
applyCredentialToRequest(baseCredential({ kind: "bearer_token", placement: { kind: "bearer_token", header: "Authorization" } }), request);
|
|
33
|
+
applyCredentialToRequest(baseCredential({ kind: "api_key", placement: { kind: "api_key", header: "X-API-Key" } }), request);
|
|
34
|
+
applyCredentialToRequest(baseCredential({ kind: "api_key", placement: { kind: "api_key", queryParam: "api_key" } }), request);
|
|
35
|
+
applyCredentialToRequest(
|
|
36
|
+
baseCredential({
|
|
37
|
+
kind: "basic_auth",
|
|
38
|
+
value: encodeCredentialValue("alice:s3cret"),
|
|
39
|
+
placement: { kind: "basic_auth", header: "Authorization" },
|
|
40
|
+
}),
|
|
41
|
+
request,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(request.headers["X-Custom-Token"]).toBe("secret");
|
|
45
|
+
expect(request.query.api_token).toBe("secret");
|
|
46
|
+
expect(request.env.GITHUB_TOKEN).toBe("secret");
|
|
47
|
+
expect(request.headers.Authorization).toBe("Basic YWxpY2U6czNjcmV0");
|
|
48
|
+
expect(request.headers["X-API-Key"]).toBe("secret");
|
|
49
|
+
expect(request.query.api_key).toBe("secret");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("throws on placement mismatch and oauth", () => {
|
|
53
|
+
const request = { headers: {}, query: {}, env: {} };
|
|
54
|
+
expect(() =>
|
|
55
|
+
applyCredentialToRequest(baseCredential({ kind: "header", placement: { kind: "query_param", queryParam: "x" } as any }), request),
|
|
56
|
+
).toThrow("Invalid credential placement");
|
|
57
|
+
|
|
58
|
+
expect(() =>
|
|
59
|
+
applyCredentialToRequest(baseCredential({ kind: "oauth" as any, placement: { kind: "oauth", header: "Authorization" } as any }), request),
|
|
60
|
+
).toThrow("not supported");
|
|
61
|
+
});
|
|
62
|
+
});
|