@klevar/portal-cli 0.1.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 (46) hide show
  1. package/.portal.env.example +3 -0
  2. package/README.md +100 -0
  3. package/dist/bin/klevar-portal.d.ts +2 -0
  4. package/dist/bin/klevar-portal.js +5 -0
  5. package/dist/bin/klevar-portal.js.map +1 -0
  6. package/dist/commands/_exemptions.d.ts +31 -0
  7. package/dist/commands/_exemptions.js +38 -0
  8. package/dist/commands/_exemptions.js.map +1 -0
  9. package/dist/commands/clients.d.ts +98 -0
  10. package/dist/commands/clients.js +17 -0
  11. package/dist/commands/clients.js.map +1 -0
  12. package/dist/commands/docs.d.ts +16 -0
  13. package/dist/commands/docs.js +5 -0
  14. package/dist/commands/docs.js.map +1 -0
  15. package/dist/commands/index.d.ts +518 -0
  16. package/dist/commands/index.js +22 -0
  17. package/dist/commands/index.js.map +1 -0
  18. package/dist/commands/metrics.d.ts +27 -0
  19. package/dist/commands/metrics.js +7 -0
  20. package/dist/commands/metrics.js.map +1 -0
  21. package/dist/commands/onboarding.d.ts +27 -0
  22. package/dist/commands/onboarding.js +7 -0
  23. package/dist/commands/onboarding.js.map +1 -0
  24. package/dist/commands/portal.d.ts +127 -0
  25. package/dist/commands/portal.js +23 -0
  26. package/dist/commands/portal.js.map +1 -0
  27. package/dist/commands/projects.d.ts +84 -0
  28. package/dist/commands/projects.js +16 -0
  29. package/dist/commands/projects.js.map +1 -0
  30. package/dist/commands/system.d.ts +57 -0
  31. package/dist/commands/system.js +12 -0
  32. package/dist/commands/system.js.map +1 -0
  33. package/dist/commands/tasks.d.ts +35 -0
  34. package/dist/commands/tasks.js +8 -0
  35. package/dist/commands/tasks.js.map +1 -0
  36. package/dist/commands/types.d.ts +12 -0
  37. package/dist/commands/types.js +2 -0
  38. package/dist/commands/types.js.map +1 -0
  39. package/dist/commands/updates.d.ts +42 -0
  40. package/dist/commands/updates.js +9 -0
  41. package/dist/commands/updates.js.map +1 -0
  42. package/dist/lib/legacy-runner.js +820 -0
  43. package/dist/portal.d.ts +1 -0
  44. package/dist/portal.js +25 -0
  45. package/dist/portal.js.map +1 -0
  46. package/package.json +29 -0
@@ -0,0 +1,3 @@
1
+ PORTAL_API_URL=http://127.0.0.1:3100
2
+ PORTAL_API_KEY=cmp_live_replace_me
3
+ PORTAL_TOKEN=
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # @klevar/portal-cli
2
+
3
+ First-class npm CLI for the Klevar Client Management Portal.
4
+
5
+ ## Install
6
+
7
+ Run without installing:
8
+
9
+ ```bash
10
+ npx klevar-portal help
11
+ ```
12
+
13
+ Install globally for repeated use:
14
+
15
+ ```bash
16
+ npm install -g @klevar/portal-cli
17
+ klevar-portal help
18
+ ```
19
+
20
+ ## Configuration
21
+
22
+ Configuration is read from environment variables or `~/.klevar/portal.env`.
23
+
24
+ ```env
25
+ PORTAL_API_URL=https://api.klevar.ai
26
+ PORTAL_API_KEY=cmp_live_replace_me
27
+ PORTAL_TOKEN=
28
+ ```
29
+
30
+ `PORTAL_API_URL` defaults to `http://127.0.0.1:3100` for local development. Use `PORTAL_TOKEN` only for client portal commands.
31
+
32
+ Do not paste API keys, portal tokens, or production secrets into chat logs, issue trackers, screenshots, or committed files.
33
+
34
+ ## Command Examples
35
+
36
+ ```bash
37
+ npx klevar-portal health
38
+ npx klevar-portal tenant info
39
+ npx klevar-portal clients list
40
+ npx klevar-portal clients create --name "Stefan" --email "stefan@example.de" --platform direct
41
+ npx klevar-portal projects create <clientId> --name "Idealo" --externalRef "idealo:ksh.de"
42
+ npx klevar-portal metrics push --external_ref "idealo:ksh.de" --source_ref "run:33" --snapshot_date "2026-04-12" --metrics '{"score":88}'
43
+ npx klevar-portal portal me --portal-token "<client-token>"
44
+ ```
45
+
46
+ ## Brain Usage
47
+
48
+ Brain usage should prefer the published `npx klevar-portal` entrypoint.
49
+
50
+ Brain agents should call:
51
+
52
+ ```bash
53
+ npx klevar-portal <resource> <action> [id] [--key value]
54
+ ```
55
+
56
+ The local compatibility wrapper remains available for old automation after a build:
57
+
58
+ ```bash
59
+ npm run cli:build
60
+ node tools/client.js clients list
61
+ ```
62
+
63
+ New automation should use `npx klevar-portal`.
64
+
65
+ ## Auth Modes
66
+
67
+ - Admin and integration commands use `X-API-Key`.
68
+ - Portal commands use `PORTAL_TOKEN` or `--portal-token`.
69
+ - Public commands such as health checks and onboarding submission do not require credentials.
70
+
71
+ Explicit no-CLI exemptions are tracked in `tools/commands/_exemptions.ts`:
72
+
73
+ - `POST /api/admin/login`, `POST /api/admin/logout`, and `GET /api/admin/me` are browser/session-only routes.
74
+ - `POST /api/tenants/register` is public bootstrap/self-registration, not a normal operational CLI action.
75
+ - `POST /api/integration/klevar-docs/events` is an inbound webhook called by Klevar Docs.
76
+
77
+ ## npm Release Flow
78
+
79
+ The npm release flow is automated in GitHub Actions and can be dry-run locally.
80
+
81
+ Dry-run locally:
82
+
83
+ ```bash
84
+ npm run cli:publish:dry
85
+ ```
86
+
87
+ Manual publish:
88
+
89
+ ```bash
90
+ npm run cli:publish
91
+ ```
92
+
93
+ GitHub Actions also provides:
94
+
95
+ - `.github/workflows/auto-publish-cli.yml`: publishes from `main` when `tools/**` changes.
96
+ - `.github/workflows/publish-cli.yml`: manual or `portal-cli-v*.*.*` tag fallback.
97
+
98
+ ## Security Notes
99
+
100
+ Do not paste API keys into shell history on shared machines. Prefer `~/.klevar/portal.env` with user-only file permissions or a secret manager. Rotate a key immediately if it appears in logs or commits.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // @ts-expect-error legacy JavaScript execution engine is copied during post-build.
3
+ await import('../lib/legacy-runner.js');
4
+ export {};
5
+ //# sourceMappingURL=klevar-portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"klevar-portal.js","sourceRoot":"","sources":["../../bin/klevar-portal.ts"],"names":[],"mappings":";AAEA,mFAAmF;AACnF,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC"}
@@ -0,0 +1,31 @@
1
+ export declare const CLI_EXEMPTIONS: readonly [{
2
+ readonly method: "POST";
3
+ readonly path: "/api/admin/login";
4
+ readonly reason: "Browser/session-only authentication route. The CLI uses API key auth.";
5
+ readonly owner: "Klevar Portal";
6
+ readonly trackedAs: "Phase 17 browser/session-only exemption";
7
+ }, {
8
+ readonly method: "POST";
9
+ readonly path: "/api/admin/logout";
10
+ readonly reason: "Browser/session-only authentication route. The CLI uses API key auth.";
11
+ readonly owner: "Klevar Portal";
12
+ readonly trackedAs: "Phase 17 browser/session-only exemption";
13
+ }, {
14
+ readonly method: "GET";
15
+ readonly path: "/api/admin/me";
16
+ readonly reason: "Session identity route is browser-only in the modular CLI contract; tenant.info covers API key identity.";
17
+ readonly owner: "Klevar Portal";
18
+ readonly trackedAs: "Phase 17 browser/session-only exemption";
19
+ }, {
20
+ readonly method: "POST";
21
+ readonly path: "/api/tenants/register";
22
+ readonly reason: "Public tenant self-registration is an installation/bootstrap capability, not an operational CLI action.";
23
+ readonly owner: "Klevar Portal";
24
+ readonly trackedAs: "Phase 17 public-bootstrap exemption";
25
+ }, {
26
+ readonly method: "POST";
27
+ readonly path: "/api/integration/klevar-docs/events";
28
+ readonly reason: "Inbound webhook consumer called by Klevar Docs, not by operators or clients.";
29
+ readonly owner: "Klevar Docs";
30
+ readonly trackedAs: "Phase 17 inbound-webhook exemption";
31
+ }];
@@ -0,0 +1,38 @@
1
+ export const CLI_EXEMPTIONS = [
2
+ {
3
+ method: 'POST',
4
+ path: '/api/admin/login',
5
+ reason: 'Browser/session-only authentication route. The CLI uses API key auth.',
6
+ owner: 'Klevar Portal',
7
+ trackedAs: 'Phase 17 browser/session-only exemption',
8
+ },
9
+ {
10
+ method: 'POST',
11
+ path: '/api/admin/logout',
12
+ reason: 'Browser/session-only authentication route. The CLI uses API key auth.',
13
+ owner: 'Klevar Portal',
14
+ trackedAs: 'Phase 17 browser/session-only exemption',
15
+ },
16
+ {
17
+ method: 'GET',
18
+ path: '/api/admin/me',
19
+ reason: 'Session identity route is browser-only in the modular CLI contract; tenant.info covers API key identity.',
20
+ owner: 'Klevar Portal',
21
+ trackedAs: 'Phase 17 browser/session-only exemption',
22
+ },
23
+ {
24
+ method: 'POST',
25
+ path: '/api/tenants/register',
26
+ reason: 'Public tenant self-registration is an installation/bootstrap capability, not an operational CLI action.',
27
+ owner: 'Klevar Portal',
28
+ trackedAs: 'Phase 17 public-bootstrap exemption',
29
+ },
30
+ {
31
+ method: 'POST',
32
+ path: '/api/integration/klevar-docs/events',
33
+ reason: 'Inbound webhook consumer called by Klevar Docs, not by operators or clients.',
34
+ owner: 'Klevar Docs',
35
+ trackedAs: 'Phase 17 inbound-webhook exemption',
36
+ }
37
+ ];
38
+ //# sourceMappingURL=_exemptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_exemptions.js","sourceRoot":"","sources":["../../commands/_exemptions.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,mBAAmB;QACzB,MAAM,EAAE,uEAAuE;QAC/E,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,eAAe;QACrB,MAAM,EAAE,0GAA0G;QAClH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,yCAAyC;KACrD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,uBAAuB;QAC7B,MAAM,EAAE,yGAAyG;QACjH,KAAK,EAAE,eAAe;QACtB,SAAS,EAAE,qCAAqC;KACjD;IACD;QACE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,qCAAqC;QAC3C,MAAM,EAAE,8EAA8E;QACtF,KAAK,EAAE,aAAa;QACpB,SAAS,EAAE,oCAAoC;KAChD;CACO,CAAC"}
@@ -0,0 +1,98 @@
1
+ export declare const clientCommands: {
2
+ 'clients.tags': {
3
+ method: "GET";
4
+ path: string;
5
+ auth: "apiKey";
6
+ description: string;
7
+ };
8
+ 'clients.list': {
9
+ method: "GET";
10
+ path: string;
11
+ auth: "apiKey";
12
+ description: string;
13
+ queryParams: string[];
14
+ };
15
+ 'clients.create': {
16
+ method: "POST";
17
+ path: string;
18
+ auth: "apiKey";
19
+ description: string;
20
+ body: string[];
21
+ };
22
+ 'clients.get': {
23
+ method: "GET";
24
+ path: string;
25
+ auth: "apiKey";
26
+ description: string;
27
+ };
28
+ 'clients.update': {
29
+ method: "PATCH";
30
+ path: string;
31
+ auth: "apiKey";
32
+ description: string;
33
+ body: string[];
34
+ };
35
+ 'clients.delete': {
36
+ method: "DELETE";
37
+ path: string;
38
+ auth: "apiKey";
39
+ description: string;
40
+ };
41
+ 'clients.purge': {
42
+ method: "DELETE";
43
+ path: string;
44
+ auth: "apiKey";
45
+ description: string;
46
+ fixedQuery: {
47
+ hard: string;
48
+ };
49
+ };
50
+ 'clients.go-live': {
51
+ method: "PATCH";
52
+ path: string;
53
+ auth: "apiKey";
54
+ description: string;
55
+ fixedBody: {
56
+ notificationsEnabled: boolean;
57
+ };
58
+ };
59
+ 'clients.mute': {
60
+ method: "PATCH";
61
+ path: string;
62
+ auth: "apiKey";
63
+ description: string;
64
+ fixedBody: {
65
+ notificationsEnabled: boolean;
66
+ };
67
+ };
68
+ 'clients.rotate-token': {
69
+ method: "POST";
70
+ path: string;
71
+ auth: "apiKey";
72
+ description: string;
73
+ };
74
+ 'token.rotate': {
75
+ method: "POST";
76
+ path: string;
77
+ auth: "apiKey";
78
+ description: string;
79
+ };
80
+ 'clients.docs-sync': {
81
+ method: "POST";
82
+ path: string;
83
+ auth: "apiKey";
84
+ description: string;
85
+ };
86
+ 'clients.docs-invoices': {
87
+ method: "GET";
88
+ path: string;
89
+ auth: "apiKey";
90
+ description: string;
91
+ };
92
+ 'clients.docs-documents': {
93
+ method: "GET";
94
+ path: string;
95
+ auth: "apiKey";
96
+ description: string;
97
+ };
98
+ };
@@ -0,0 +1,17 @@
1
+ export const clientCommands = {
2
+ 'clients.tags': { method: 'GET', path: '/api/admin/clients/tags', auth: 'apiKey', description: 'List client tags' },
3
+ 'clients.list': { method: 'GET', path: '/api/admin/clients', auth: 'apiKey', description: 'List clients', queryParams: ['status', 'platform', 'tier', 'tag'] },
4
+ 'clients.create': { method: 'POST', path: '/api/admin/clients', auth: 'apiKey', description: 'Create a client', body: ['name', 'email', 'company', 'phone', 'platform', 'platformId', 'tier', 'currency', 'notes', 'tags'] },
5
+ 'clients.get': { method: 'GET', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Get a client' },
6
+ 'clients.update': { method: 'PATCH', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Update a client', body: ['name', 'email', 'company', 'phone', 'platform', 'platformId', 'status', 'tier', 'currency', 'notes', 'tags', 'notificationsEnabled'] },
7
+ 'clients.delete': { method: 'DELETE', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Delete or churn a client' },
8
+ 'clients.purge': { method: 'DELETE', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Hard-delete client and related data', fixedQuery: { hard: 'true' } },
9
+ 'clients.go-live': { method: 'PATCH', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Enable notifications', fixedBody: { notificationsEnabled: true } },
10
+ 'clients.mute': { method: 'PATCH', path: '/api/admin/clients/:id', auth: 'apiKey', description: 'Disable notifications', fixedBody: { notificationsEnabled: false } },
11
+ 'clients.rotate-token': { method: 'POST', path: '/api/admin/clients/:id/rotate-token', auth: 'apiKey', description: 'Rotate portal token' },
12
+ 'token.rotate': { method: 'POST', path: '/api/admin/clients/:id/rotate-token', auth: 'apiKey', description: 'Rotate portal token' },
13
+ 'clients.docs-sync': { method: 'POST', path: '/api/admin/clients/:id/docs-sync', auth: 'apiKey', description: 'Sync client to Docs' },
14
+ 'clients.docs-invoices': { method: 'GET', path: '/api/admin/clients/:id/docs/invoices', auth: 'apiKey', description: 'List client invoices' },
15
+ 'clients.docs-documents': { method: 'GET', path: '/api/admin/clients/:id/docs/documents', auth: 'apiKey', description: 'List client documents' },
16
+ };
17
+ //# sourceMappingURL=clients.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.js","sourceRoot":"","sources":["../../commands/clients.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,cAAc,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,yBAAyB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE;IACnH,cAAc,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;IAC9J,gBAAgB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE;IAC5N,aAAa,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE;IAC7G,gBAAgB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,sBAAsB,CAAC,EAAE;IACnQ,gBAAgB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0BAA0B,EAAE;IAC/H,eAAe,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qCAAqC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IACvK,iBAAiB,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE,SAAS,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,EAAE;IACtK,cAAc,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,wBAAwB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE,SAAS,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE,EAAE;IACrK,sBAAsB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,qCAAqC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;IAC3I,cAAc,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,qCAAqC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACnI,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qBAAqB,EAAE;IACrI,uBAAuB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;IAC7I,wBAAwB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,uCAAuC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uBAAuB,EAAE;CAC1H,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const docsCommands: {
2
+ 'docs.document-download': {
3
+ method: "DOWNLOAD";
4
+ path: string;
5
+ auth: "apiKey";
6
+ description: string;
7
+ body: string[];
8
+ };
9
+ 'docs.invoice-download': {
10
+ method: "DOWNLOAD";
11
+ path: string;
12
+ auth: "apiKey";
13
+ description: string;
14
+ body: string[];
15
+ };
16
+ };
@@ -0,0 +1,5 @@
1
+ export const docsCommands = {
2
+ 'docs.document-download': { method: 'DOWNLOAD', path: '/api/admin/docs/documents/:id/download', auth: 'apiKey', description: 'Download document', body: ['output'] },
3
+ 'docs.invoice-download': { method: 'DOWNLOAD', path: '/api/admin/docs/invoices/:id/download', auth: 'apiKey', description: 'Download invoice', body: ['output'] },
4
+ };
5
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../commands/docs.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,wBAAwB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,wCAAwC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE;IACpK,uBAAuB,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,uCAAuC,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE;CAC3I,CAAC"}