@epilot/cli 0.1.108 → 0.1.109
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 +1 -1
- package/definitions/app.json +170 -38
- package/dist/{add-component-UPHG3VNG.js → add-component-TCF7V3KU.js} +1 -11
- package/dist/add-function-VRVBRNY3.js +184 -0
- package/dist/app-NATUF3YX.js +26 -0
- package/dist/bin/epilot.js +4 -4
- package/dist/{chunk-QOD77YLW.js → chunk-UDGF4AVJ.js} +263 -3
- package/dist/{deploy-NRQHZ635.js → deploy-2U5FVEE7.js} +90 -9
- package/dist/dev-JEAHUYQU.js +105 -0
- package/dist/{export-ZRDJCALM.js → export-VLAY2KZP.js} +1 -1
- package/dist/{init-BXAGJAPS.js → init-C66L5GFR.js} +164 -4
- package/dist/{remove-component-LPTHVN4P.js → remove-component-7C7MPSBF.js} +1 -1
- package/dist/{review-OZTM3XBD.js → review-JZTMQNU3.js} +1 -1
- package/dist/{upgrade-HU256V6J.js → upgrade-5RFPLKLG.js} +1 -1
- package/dist/{validate-TLSOTJAY.js → validate-J35DJW3H.js} +9 -1
- package/dist/{versions-VA4H3EPK.js → versions-2TMTHO7W.js} +1 -1
- package/package.json +1 -1
- package/dist/app-RULTIGMJ.js +0 -24
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
log,
|
|
4
4
|
writeManifest
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UDGF4AVJ.js";
|
|
6
6
|
import "./chunk-M3M3C5WH.js";
|
|
7
7
|
import "./chunk-YHQA2AVG.js";
|
|
8
8
|
import "./chunk-7ZQ666ZQ.js";
|
|
@@ -108,6 +108,9 @@ var init_default = defineCommand({
|
|
|
108
108
|
"- `CUSTOM_FLOW_ACTION_EXTERNAL` \u2014 External integration webhook",
|
|
109
109
|
"- `PORTAL_EXTENSION` \u2014 Portal extension hooks",
|
|
110
110
|
"- `EXTERNAL_PRODUCT_CATALOG` \u2014 External product catalog hooks",
|
|
111
|
+
"",
|
|
112
|
+
"Inline in manifest.json (no directory):",
|
|
113
|
+
"- `API_PROXY` \u2014 Server-side API proxy with credential injection",
|
|
111
114
|
""
|
|
112
115
|
].join("\n")
|
|
113
116
|
);
|
|
@@ -250,6 +253,21 @@ Deploy the app:
|
|
|
250
253
|
|
|
251
254
|
The deploy command reads \`components/<dir>/configuration.json\` at deploy time and uses it as the component's configuration. This means you edit config in the component directory, not in manifest.json.
|
|
252
255
|
|
|
256
|
+
**Deploy does not update existing installations.** Installations keep a frozen snapshot of
|
|
257
|
+
components, options and grants. After deploying to an app that is already installed:
|
|
258
|
+
|
|
259
|
+
\`\`\`bash
|
|
260
|
+
epilot app api patchInstallation <appId> -d '{"version":"<version>"}'
|
|
261
|
+
\`\`\`
|
|
262
|
+
|
|
263
|
+
This refreshes components and grants **and preserves configured option values**
|
|
264
|
+
(uninstall/reinstall loses them; \`promote-to\` to the already-installed version is a silent
|
|
265
|
+
no-op). The patch resets \`enabled\` to \`false\` \u2014 an org admin must re-save the app
|
|
266
|
+
configuration in the epilot UI afterwards.
|
|
267
|
+
|
|
268
|
+
**Auth tokens expire after ~1 hour.** A mid-deploy 403 that reads like a permissions problem
|
|
269
|
+
is usually just an expired token \u2014 run \`epilot auth login\` and deploy again.
|
|
270
|
+
|
|
253
271
|
### \`epilot app export --app-id <id> [-o manifest.json]\`
|
|
254
272
|
Export an existing app from the API as a manifest.json.
|
|
255
273
|
|
|
@@ -440,9 +458,95 @@ Hooks that provide products from an external catalog to epilot Journeys.
|
|
|
440
458
|
|
|
441
459
|
**Hook types:** \`products\`, \`product-recommendations\`.
|
|
442
460
|
|
|
461
|
+
### Inline components (no directory at all)
|
|
462
|
+
|
|
463
|
+
#### API_PROXY
|
|
464
|
+
A server-side proxy that lets your frontend components call an external API without exposing
|
|
465
|
+
credentials to the browser. Added with \`epilot app add-component <name> --type API_PROXY\`
|
|
466
|
+
(prompts for proxy name, target URL and auth type). Unlike every other component type it has
|
|
467
|
+
**no \`_dir\` and no folder** \u2014 it lives inline in \`manifest.json\`:
|
|
468
|
+
|
|
469
|
+
\`\`\`json
|
|
470
|
+
{
|
|
471
|
+
"component_type": "API_PROXY",
|
|
472
|
+
"configuration": {
|
|
473
|
+
"name": "sap",
|
|
474
|
+
"target": "https://my-gateway.example.com",
|
|
475
|
+
"auth_type": "header"
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
\`\`\`
|
|
479
|
+
|
|
480
|
+
Call it from the frontend with the \`@epilot/app-sdk\` helper (do not hand-roll fetch):
|
|
481
|
+
|
|
482
|
+
\`\`\`ts
|
|
483
|
+
import { proxy } from '@epilot/app-sdk';
|
|
484
|
+
|
|
485
|
+
const data = await proxy('sap', '/API_BUSINESS_PARTNER/A_BusinessPartner', {
|
|
486
|
+
appId,
|
|
487
|
+
token, // App Bridge session token
|
|
488
|
+
});
|
|
489
|
+
\`\`\`
|
|
490
|
+
|
|
491
|
+
Current limits \u2014 probe your target with curl through the proxy before building on it:
|
|
492
|
+
|
|
493
|
+
- **Query parameters are rejected** (400) by the proxy route's own request validation.
|
|
494
|
+
Workaround until fixed: percent-encode the \`?\` into the path
|
|
495
|
+
(\`/path%3F$expand=to_Customer\`) \u2014 fragile, leave a comment where you do it.
|
|
496
|
+
- Only **GET and POST** are proxied \u2014 no PUT/PATCH/DELETE.
|
|
497
|
+
- \`auth_type\` is \`none | header | bearer | oauth2\` \u2014 there is **no \`basic\`**. For HTTP Basic
|
|
498
|
+
targets, add one secret option holding a pre-computed Base64 \`user:pass\` and configure
|
|
499
|
+
\`"headers": { "Authorization": "Basic {{my_basic_secret}}" }\`.
|
|
500
|
+
|
|
501
|
+
## Local Development (Dev Mode)
|
|
502
|
+
|
|
503
|
+
\`npm run dev\` alone is of limited use for capabilities/pages: outside the epilot iframe there
|
|
504
|
+
is no token and no entity. **Dev mode** makes epilot load your component from localhost inside
|
|
505
|
+
the real epilot UI (localhost is a trustworthy origin, so the HTTPS iframe allows it).
|
|
506
|
+
|
|
507
|
+
\`\`\`bash
|
|
508
|
+
epilot app dev # enable (default URL http://localhost:5173)
|
|
509
|
+
epilot app dev -c my-tab -u http://localhost:3000 # pick component and URL
|
|
510
|
+
epilot app dev --off # disable \u2014 required before cloning a version
|
|
511
|
+
\`\`\`
|
|
512
|
+
|
|
513
|
+
Then \`npm run dev\` in the component folder and reload the entity page in epilot.
|
|
514
|
+
|
|
515
|
+
If your CLI version predates \`app dev\`, it wraps these two raw API patches:
|
|
516
|
+
|
|
517
|
+
\`\`\`bash
|
|
518
|
+
# 1. Enable dev mode on the app
|
|
519
|
+
epilot app api patchMetadata <appId> -d '{"dev_mode": true}'
|
|
520
|
+
|
|
521
|
+
# 2. Add the override URL to the component.
|
|
522
|
+
# The PATCH does NOT merge \u2014 fetch the full component object first and re-send
|
|
523
|
+
# ALL of it with the override added, or you get: 400 must have required property 'id'.
|
|
524
|
+
epilot app api getConfiguration <appId>
|
|
525
|
+
epilot app api patchComponent <appId> <version> <componentId> -d '<full component + override>'
|
|
526
|
+
\`\`\`
|
|
527
|
+
|
|
528
|
+
The override location **depends on the component type**:
|
|
529
|
+
|
|
530
|
+
| Component type | Override field |
|
|
531
|
+
| --- | --- |
|
|
532
|
+
| \`CUSTOM_CAPABILITY\` (and other zip surfaces) | \`surfaces.capability_config.override_url\` |
|
|
533
|
+
| \`CUSTOM_JOURNEY_BLOCK\` | \`configuration.override_dev_mode.override_url\` |
|
|
534
|
+
|
|
535
|
+
Do not copy the journey-block shape onto a capability \u2014 it silently does nothing.
|
|
536
|
+
|
|
537
|
+
To leave dev mode, set \`dev_mode: false\` and remove the override (again re-sending the full
|
|
538
|
+
component).
|
|
539
|
+
|
|
540
|
+
Note: the dev-mode switch in the epilot portal UI is a different mechanism and does not write
|
|
541
|
+
these fields.
|
|
542
|
+
|
|
443
543
|
## Workflow: Building an App from Scratch
|
|
444
544
|
|
|
445
545
|
\`\`\`bash
|
|
546
|
+
# 0. If the app calls an external API: probe it with curl FIRST.
|
|
547
|
+
# Get auth + one real response working in the terminal before writing any code.
|
|
548
|
+
# (Through the proxy too, once deployed \u2014 its limits bite real targets, see API_PROXY.)
|
|
549
|
+
|
|
446
550
|
# 1. Scaffold the project
|
|
447
551
|
epilot app init my-app
|
|
448
552
|
cd my-app
|
|
@@ -492,16 +596,72 @@ For every component with a \`_dir\` field, the CLI reads \`components/<dir>/conf
|
|
|
492
596
|
5. Injects the CDN URL into the component configuration/surfaces before upserting
|
|
493
597
|
|
|
494
598
|
### Permissions
|
|
495
|
-
|
|
599
|
+
|
|
600
|
+
Apps request permissions via the \`permissions\` array in the manifest. On installation, epilot
|
|
601
|
+
creates a role with these grants in the installing organization. Common actions:
|
|
602
|
+
\`entity:read\`, \`entity:write\`, \`entity:delete\`, \`workflow:read\`, \`workflow:write\`.
|
|
603
|
+
|
|
604
|
+
What permissions do and do not cover:
|
|
605
|
+
|
|
606
|
+
- They gate the app's **server-side** access to epilot APIs.
|
|
607
|
+
- They do **not** make the App Bridge token work against the Entity API \u2014 that call returns
|
|
608
|
+
403 even with the permission granted. To show the current entity's data in a capability,
|
|
609
|
+
read it from the App Bridge context (see the App Bridge section); that path needs **no
|
|
610
|
+
permission at all**. Only request \`entity:*\` permissions for genuine server-side API use.
|
|
611
|
+
- **Every \`epilot app deploy\` re-patches the version's grants and desyncs existing
|
|
612
|
+
installations** \u2014 the installed app starts getting 403s while the permissions UI still
|
|
613
|
+
shows everything granted. After deploying to an already-installed app, re-sync with
|
|
614
|
+
\`epilot app api patchInstallation <appId> -d '{"version":"<version>"}'\`, then have an org
|
|
615
|
+
admin re-save the app configuration in epilot (the patch resets \`enabled\` to false).
|
|
496
616
|
|
|
497
617
|
### Options
|
|
498
|
-
Components can declare \`options\` \u2014 configuration values set by the installing organization. Types: \`text\`, \`number\`, \`boolean\`, \`secret\`.
|
|
618
|
+
Components can declare \`options\` \u2014 configuration values set by the installing organization. Types: \`text\`, \`number\`, \`boolean\`, \`secret\`. Use \`{{option_key}}\` in configuration URLs/headers for interpolation.
|
|
619
|
+
|
|
620
|
+
Two caveats:
|
|
621
|
+
- **Options merge on upsert.** Deploying a component without an \`options\` key leaves the old
|
|
622
|
+
options in place, so removed options stay visible to installing admins. Send \`"options": []\`
|
|
623
|
+
once to clear them, then patch the installation.
|
|
624
|
+
- **"Secret" means not delivered to the app's browser code.** Secret values are never stored
|
|
625
|
+
in the manifest and are injected server-side \u2014 but organization admins with API access can
|
|
626
|
+
read stored values back. Do not describe them to users as unreadable.
|
|
499
627
|
|
|
500
628
|
### Descriptions
|
|
501
629
|
All user-facing text (app name, component names, descriptions) must include a \`de\` (German) translation. \`en\` is optional but recommended.
|
|
502
630
|
|
|
503
631
|
### App Bridge
|
|
504
|
-
|
|
632
|
+
|
|
633
|
+
Capabilities, pages and portal blocks run in an iframe and talk to epilot via
|
|
634
|
+
\`@epilot/app-bridge\`. Wrap your app in an \`AppBridgeProvider\`.
|
|
635
|
+
|
|
636
|
+
**The context already contains the entity.** \`getEntityContext()\` returns the full entity
|
|
637
|
+
next to its id (the \`EntityContext\` type may not declare the \`entity\` field yet \u2014 it is
|
|
638
|
+
there at runtime). A tab that renders entity data needs no Entity API call and no
|
|
639
|
+
\`entity:read\` permission:
|
|
640
|
+
|
|
641
|
+
\`\`\`ts
|
|
642
|
+
const ctx = await getEntityContext()
|
|
643
|
+
ctx.entityId // '53a9f8c4-\u2026'
|
|
644
|
+
(ctx as any).entity // the whole entity \u2014 prefer this over fetching it
|
|
645
|
+
\`\`\`
|
|
646
|
+
|
|
647
|
+
Prefer this over the Entity API: it is one round-trip cheaper, and app tokens are **not**
|
|
648
|
+
guaranteed to authorise direct Entity API calls (expect 403 there even when the permission
|
|
649
|
+
shows as granted).
|
|
650
|
+
|
|
651
|
+
**\`initialize()\` makes one attempt and rejects after \`timeout\` (default 5000 ms).** There is
|
|
652
|
+
no internal retry, so if the parent is not listening yet the message is lost. Retry 2\u20133 times
|
|
653
|
+
before concluding you are outside epilot, and keep the session token even when a later
|
|
654
|
+
context request fails \u2014 otherwise "epilot answered oddly" is indistinguishable from "no epilot".
|
|
655
|
+
|
|
656
|
+
**Report your height.** The iframe does not auto-size. Call \`updateContentHeight(px)\` from a
|
|
657
|
+
\`ResizeObserver\` on \`document.body\`. Do **not** set \`html, body, #root { height: 100% }\` while
|
|
658
|
+
measuring \`scrollHeight\` \u2014 the body then can never exceed the iframe, so the reported height
|
|
659
|
+
never grows and the tab stays a small scrolling box.
|
|
660
|
+
|
|
661
|
+
**Refresh on becoming visible.** \`onVisibilityChange(cb)\` fires when the user switches tabs.
|
|
662
|
+
|
|
663
|
+
**Debugging.** Your console output goes to the iframe's JS context (\`cdn.app.sls.epilot.io\`),
|
|
664
|
+
not \`top\`. Switch the DevTools context selector or you will see nothing and assume nothing ran.
|
|
505
665
|
|
|
506
666
|
### Volt UI
|
|
507
667
|
Use \`@epilot/volt-ui\` for UI components in App Bridge surfaces (capabilities, pages, portal blocks). It provides cards, buttons, forms, selectors, and more \u2014 consistent with epilot's design system.
|
|
@@ -72,7 +72,7 @@ ${GREEN}${BOLD}Upgraded to @epilot/cli@${latest}${RESET}
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
var getCurrentVersion = () => {
|
|
75
|
-
if (true) return "0.1.
|
|
75
|
+
if (true) return "0.1.109";
|
|
76
76
|
try {
|
|
77
77
|
const output = execSync("npm ls -g @epilot/cli --depth=0 --json 2>/dev/null", {
|
|
78
78
|
encoding: "utf-8",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
log,
|
|
4
4
|
validateManifest
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UDGF4AVJ.js";
|
|
6
6
|
import "./chunk-M3M3C5WH.js";
|
|
7
7
|
import "./chunk-YHQA2AVG.js";
|
|
8
8
|
import "./chunk-7ZQ666ZQ.js";
|
|
@@ -40,6 +40,14 @@ var validate_default = defineCommand({
|
|
|
40
40
|
const manifest = result.manifest;
|
|
41
41
|
log.success(`${manifestPath} is valid`);
|
|
42
42
|
log.info(`${manifest.components.length} component(s) defined`);
|
|
43
|
+
if (manifest.functions?.length) {
|
|
44
|
+
const scheduled = manifest.functions.filter((f) => f.type === "scheduled");
|
|
45
|
+
const workflow = manifest.functions.filter((f) => f.type === "workflow");
|
|
46
|
+
log.info(`${manifest.functions.length} function(s): ${workflow.length} workflow, ${scheduled.length} scheduled`);
|
|
47
|
+
for (const fn of scheduled) {
|
|
48
|
+
log.dim(`${fn.name}: ${fn.schedule}${fn.schedule_timezone ? ` (${fn.schedule_timezone})` : ""}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
43
51
|
const secretOptions = manifest.components.flatMap((c) => (c.options ?? []).filter((o) => o.type === "secret"));
|
|
44
52
|
if (secretOptions.length > 0) log.info(`${secretOptions.length} secret option(s) (set per-installation)`);
|
|
45
53
|
if (manifest.assets?.logo) log.info(`Logo: ${manifest.assets.logo}`);
|
package/package.json
CHANGED
package/dist/app-RULTIGMJ.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/commands/app/index.ts
|
|
4
|
-
import { defineCommand } from "citty";
|
|
5
|
-
var app_default = defineCommand({
|
|
6
|
-
meta: {
|
|
7
|
-
name: "app",
|
|
8
|
-
description: "Manage epilot Apps \u2014 create, deploy, and manage app manifests"
|
|
9
|
-
},
|
|
10
|
-
subCommands: {
|
|
11
|
-
init: () => import("./init-BXAGJAPS.js").then((m) => m.default),
|
|
12
|
-
"add-component": () => import("./add-component-UPHG3VNG.js").then((m) => m.default),
|
|
13
|
-
"remove-component": () => import("./remove-component-LPTHVN4P.js").then((m) => m.default),
|
|
14
|
-
validate: () => import("./validate-TLSOTJAY.js").then((m) => m.default),
|
|
15
|
-
deploy: () => import("./deploy-NRQHZ635.js").then((m) => m.default),
|
|
16
|
-
export: () => import("./export-ZRDJCALM.js").then((m) => m.default),
|
|
17
|
-
versions: () => import("./versions-VA4H3EPK.js").then((m) => m.default),
|
|
18
|
-
review: () => import("./review-OZTM3XBD.js").then((m) => m.default),
|
|
19
|
-
api: () => import("./api-5W2UMWCW.js").then((m) => m.default)
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
export {
|
|
23
|
-
app_default as default
|
|
24
|
-
};
|