octwin-cli 0.1.4 → 0.1.5
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/LICENSE +21 -21
- package/README.md +133 -133
- package/dist/index.js +275 -27
- package/package.json +36 -36
- package/templates/starter/commands.yaml +14 -14
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 CEQUENS
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CEQUENS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
# octwin-cli
|
|
2
|
-
|
|
3
|
-
> The **Octwin** external-pack developer CLI — scaffold, validate, deploy, and manage pure-YAML packs on your own tenant. By **CEQUENS**.
|
|
4
|
-
|
|
5
|
-
[](https://www.npmjs.com/package/octwin-cli)
|
|
6
|
-
[](https://www.npmjs.com/package/octwin-cli)
|
|
7
|
-
[](https://nodejs.org)
|
|
8
|
-
|
|
9
|
-
Octwin is a pack-pluggable conversational-agent platform for WhatsApp and web. A **pack** is a
|
|
10
|
-
self-contained bot domain — its agent, conversation flows, prompts, and data model — declared
|
|
11
|
-
entirely in YAML. `octwin-cli` lets you build a **pure-YAML pack** in your own repo and deploy it
|
|
12
|
-
to a running Octwin platform to test live on your own tenant: no platform checkout, no build step,
|
|
13
|
-
and nothing untrusted to run (a pure-YAML pack is declarative data, so the platform can safely run
|
|
14
|
-
it alongside other tenants).
|
|
15
|
-
|
|
16
|
-
> The npm package is **`octwin-cli`**; the command it installs is **`octwin`**.
|
|
17
|
-
|
|
18
|
-
## Requirements
|
|
19
|
-
|
|
20
|
-
- **Node.js ≥ 20**
|
|
21
|
-
- Access to an **Octwin platform** (its base URL), a **tenant** (your workspace) on it, and a
|
|
22
|
-
**deploy token** (generated in the Octwin console — see [Authentication](#authentication)).
|
|
23
|
-
|
|
24
|
-
## Install
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
# zero-install — always the latest version
|
|
28
|
-
npx octwin-cli@latest <command>
|
|
29
|
-
|
|
30
|
-
# …or install the `octwin` command globally
|
|
31
|
-
npm install -g octwin-cli
|
|
32
|
-
octwin <command>
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Quick start
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
# 1. Scaffold a standalone pure-YAML pack (this is your repo)
|
|
39
|
-
octwin init ./my-pack --id my-pack --description "My business bot"
|
|
40
|
-
cd ./my-pack
|
|
41
|
-
git init && git add -A && git commit -m "init pack"
|
|
42
|
-
|
|
43
|
-
# 2. Author it — edit manifest.yaml, flows/tools/main.flow.yaml (+ its locale),
|
|
44
|
-
# and prompts/identity.md. Everything is pure YAML.
|
|
45
|
-
|
|
46
|
-
# 3. Point it at your platform: edit pack.json, then log in with a deploy token
|
|
47
|
-
# (Octwin console → your workspace → API tokens → Generate)
|
|
48
|
-
octwin login --url https://your-octwin.example.com --token oct_…
|
|
49
|
-
|
|
50
|
-
# 4. Validate → deploy → confirm it's live
|
|
51
|
-
octwin validate
|
|
52
|
-
octwin deploy --seed # --seed also loads any demo data the pack declares
|
|
53
|
-
octwin status # "✓ live and current" once it's warm
|
|
54
|
-
|
|
55
|
-
# 5. Chat with it on your tenant (web widget / console test page). Edit and
|
|
56
|
-
# `octwin deploy` again — a redeploy hot-loads with no restart.
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Commands
|
|
60
|
-
|
|
61
|
-
| Command | What it does |
|
|
62
|
-
| --- | --- |
|
|
63
|
-
| `octwin init <dir>` | Scaffold a new pure-YAML pack into `<dir>` (writes a starter `manifest.yaml`, flow, prompt, and `pack.json`). Options: `--id`, `--description`, `--display-name`. |
|
|
64
|
-
| `octwin validate` | Check the pack locally (structure + pure-YAML rules). The platform re-validates the full manifest/flow schema on deploy. |
|
|
65
|
-
| `octwin login` | Save a deploy token for a platform URL (stored in `~/.octwin/credentials.json`). `--url`, `--token`. |
|
|
66
|
-
| `octwin whoami` | Verify the saved/passed token is valid for a tenant. `--url`, `--tenant`. |
|
|
67
|
-
| `octwin deploy` | Upload + install the pack onto your tenant's project. `--seed` also runs the pack's demo seed. |
|
|
68
|
-
| `octwin status` | Report what the platform has live for this pack — installed vs. loaded version, and its flows. |
|
|
69
|
-
| `octwin platform-kb pull` | Pull the platform's capability reference (built-ins, primitives, render intents, flow-DSL — as markdown + JSON) into `.octwin/platform-kb/`, for the **`octwin-pack`** Claude Code authoring plugin to consult. |
|
|
70
|
-
| `octwin test` | Validate locally and print how to try the pack on your tenant. |
|
|
71
|
-
| `octwin help` | Show usage. |
|
|
72
|
-
|
|
73
|
-
Every command that talks to the platform accepts `--dir <path>` (the pack directory; defaults to
|
|
74
|
-
the current directory) plus the target overrides `--url` / `--tenant` / `--project` / `--token`.
|
|
75
|
-
|
|
76
|
-
## Configuration
|
|
77
|
-
|
|
78
|
-
The deploy target has four settings. Three live in a committed **`pack.json`** at the root of your
|
|
79
|
-
pack; the **token is a secret** and is kept out of `pack.json` (set it with `octwin login`).
|
|
80
|
-
|
|
81
|
-
```jsonc
|
|
82
|
-
// pack.json — created by `octwin init`
|
|
83
|
-
{
|
|
84
|
-
"platform_url": "https://your-octwin.example.com", // your Octwin platform
|
|
85
|
-
"tenant": "my-tenant-slug", // your workspace (required)
|
|
86
|
-
"project": "main" // defaults to "main"
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
octwin login --url https://your-octwin.example.com --token oct_… # saves the token once
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Each setting resolves in this order — **flag → environment variable → `pack.json` → default**:
|
|
95
|
-
|
|
96
|
-
| Setting | Flag | Env var | `pack.json` key |
|
|
97
|
-
| --- | --- | --- | --- |
|
|
98
|
-
| Platform URL | `--url` | `PACK_PLATFORM_URL` | `platform_url` |
|
|
99
|
-
| Tenant slug | `--tenant` | `PACK_TENANT` | `tenant` |
|
|
100
|
-
| Project slug | `--project` | `PACK_PROJECT` | `project` (default `main`) |
|
|
101
|
-
| Deploy token | `--token` | `PACK_TOKEN` | — *(use `octwin login`)* |
|
|
102
|
-
|
|
103
|
-
For **CI**, skip `pack.json`/`login` entirely and pass `PACK_PLATFORM_URL`, `PACK_TENANT`,
|
|
104
|
-
`PACK_PROJECT`, and `PACK_TOKEN` as environment variables.
|
|
105
|
-
|
|
106
|
-
## Authentication
|
|
107
|
-
|
|
108
|
-
You authenticate with a tenant-scoped **deploy token** (prefixed `oct_…`) — not a password and not
|
|
109
|
-
an operator token. Generate it in the Octwin console (**your workspace → API tokens → Generate**).
|
|
110
|
-
It is **least-privilege** (scope `pack:deploy`): it can deploy packs to your tenant but cannot
|
|
111
|
-
manage members, billing, or other tenants, and it is revocable at any time.
|
|
112
|
-
|
|
113
|
-
Add the optional **`media:generate`** scope to let a `--seed` deploy AI-generate seed images
|
|
114
|
-
(for a demo record field like `photo: "generate:<prompt>"`); without it, such fields are seeded as
|
|
115
|
-
text only.
|
|
116
|
-
|
|
117
|
-
## What a pack may contain
|
|
118
|
-
|
|
119
|
-
A pack is **pure declarative data** — `.yaml` / `.yml` / `.md` / `.sql` / `.json` only. Executable
|
|
120
|
-
code (`.ts`/`.js`), HTTP routes, DB clients, and custom primitives are **not** allowed (this is what
|
|
121
|
-
makes an external pack safe to run on a shared platform; the server enforces it on deploy). For
|
|
122
|
-
domain records, use Octwin's first-class storage modules — **XRM** (records with stage pipelines),
|
|
123
|
-
**catalog** (products), or **casework** (tickets) — declared in `xrm.yaml` / `cases.yaml`, so a pack
|
|
124
|
-
needs **no database of its own**.
|
|
125
|
-
|
|
126
|
-
## Links
|
|
127
|
-
|
|
128
|
-
- **npm:** <https://www.npmjs.com/package/octwin-cli>
|
|
129
|
-
- **Command help:** `octwin help`
|
|
130
|
-
|
|
131
|
-
## License
|
|
132
|
-
|
|
133
|
-
[MIT](./LICENSE) © CEQUENS
|
|
1
|
+
# octwin-cli
|
|
2
|
+
|
|
3
|
+
> The **Octwin** external-pack developer CLI — scaffold, validate, deploy, and manage pure-YAML packs on your own tenant. By **CEQUENS**.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/octwin-cli)
|
|
6
|
+
[](https://www.npmjs.com/package/octwin-cli)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
|
|
9
|
+
Octwin is a pack-pluggable conversational-agent platform for WhatsApp and web. A **pack** is a
|
|
10
|
+
self-contained bot domain — its agent, conversation flows, prompts, and data model — declared
|
|
11
|
+
entirely in YAML. `octwin-cli` lets you build a **pure-YAML pack** in your own repo and deploy it
|
|
12
|
+
to a running Octwin platform to test live on your own tenant: no platform checkout, no build step,
|
|
13
|
+
and nothing untrusted to run (a pure-YAML pack is declarative data, so the platform can safely run
|
|
14
|
+
it alongside other tenants).
|
|
15
|
+
|
|
16
|
+
> The npm package is **`octwin-cli`**; the command it installs is **`octwin`**.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- **Node.js ≥ 20**
|
|
21
|
+
- Access to an **Octwin platform** (its base URL), a **tenant** (your workspace) on it, and a
|
|
22
|
+
**deploy token** (generated in the Octwin console — see [Authentication](#authentication)).
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# zero-install — always the latest version
|
|
28
|
+
npx octwin-cli@latest <command>
|
|
29
|
+
|
|
30
|
+
# …or install the `octwin` command globally
|
|
31
|
+
npm install -g octwin-cli
|
|
32
|
+
octwin <command>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 1. Scaffold a standalone pure-YAML pack (this is your repo)
|
|
39
|
+
octwin init ./my-pack --id my-pack --description "My business bot"
|
|
40
|
+
cd ./my-pack
|
|
41
|
+
git init && git add -A && git commit -m "init pack"
|
|
42
|
+
|
|
43
|
+
# 2. Author it — edit manifest.yaml, flows/tools/main.flow.yaml (+ its locale),
|
|
44
|
+
# and prompts/identity.md. Everything is pure YAML.
|
|
45
|
+
|
|
46
|
+
# 3. Point it at your platform: edit pack.json, then log in with a deploy token
|
|
47
|
+
# (Octwin console → your workspace → API tokens → Generate)
|
|
48
|
+
octwin login --url https://your-octwin.example.com --token oct_…
|
|
49
|
+
|
|
50
|
+
# 4. Validate → deploy → confirm it's live
|
|
51
|
+
octwin validate
|
|
52
|
+
octwin deploy --seed # --seed also loads any demo data the pack declares
|
|
53
|
+
octwin status # "✓ live and current" once it's warm
|
|
54
|
+
|
|
55
|
+
# 5. Chat with it on your tenant (web widget / console test page). Edit and
|
|
56
|
+
# `octwin deploy` again — a redeploy hot-loads with no restart.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Commands
|
|
60
|
+
|
|
61
|
+
| Command | What it does |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `octwin init <dir>` | Scaffold a new pure-YAML pack into `<dir>` (writes a starter `manifest.yaml`, flow, prompt, and `pack.json`). Options: `--id`, `--description`, `--display-name`. |
|
|
64
|
+
| `octwin validate` | Check the pack locally (structure + pure-YAML rules). The platform re-validates the full manifest/flow schema on deploy. |
|
|
65
|
+
| `octwin login` | Save a deploy token for a platform URL (stored in `~/.octwin/credentials.json`). `--url`, `--token`. |
|
|
66
|
+
| `octwin whoami` | Verify the saved/passed token is valid for a tenant. `--url`, `--tenant`. |
|
|
67
|
+
| `octwin deploy` | Upload + install the pack onto your tenant's project. `--seed` also runs the pack's demo seed. |
|
|
68
|
+
| `octwin status` | Report what the platform has live for this pack — installed vs. loaded version, and its flows. |
|
|
69
|
+
| `octwin platform-kb pull` | Pull the platform's capability reference (built-ins, primitives, render intents, flow-DSL — as markdown + JSON) into `.octwin/platform-kb/`, for the **`octwin-pack`** Claude Code authoring plugin to consult. |
|
|
70
|
+
| `octwin test` | Validate locally and print how to try the pack on your tenant. |
|
|
71
|
+
| `octwin help` | Show usage. |
|
|
72
|
+
|
|
73
|
+
Every command that talks to the platform accepts `--dir <path>` (the pack directory; defaults to
|
|
74
|
+
the current directory) plus the target overrides `--url` / `--tenant` / `--project` / `--token`.
|
|
75
|
+
|
|
76
|
+
## Configuration
|
|
77
|
+
|
|
78
|
+
The deploy target has four settings. Three live in a committed **`pack.json`** at the root of your
|
|
79
|
+
pack; the **token is a secret** and is kept out of `pack.json` (set it with `octwin login`).
|
|
80
|
+
|
|
81
|
+
```jsonc
|
|
82
|
+
// pack.json — created by `octwin init`
|
|
83
|
+
{
|
|
84
|
+
"platform_url": "https://your-octwin.example.com", // your Octwin platform
|
|
85
|
+
"tenant": "my-tenant-slug", // your workspace (required)
|
|
86
|
+
"project": "main" // defaults to "main"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
octwin login --url https://your-octwin.example.com --token oct_… # saves the token once
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Each setting resolves in this order — **flag → environment variable → `pack.json` → default**:
|
|
95
|
+
|
|
96
|
+
| Setting | Flag | Env var | `pack.json` key |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| Platform URL | `--url` | `PACK_PLATFORM_URL` | `platform_url` |
|
|
99
|
+
| Tenant slug | `--tenant` | `PACK_TENANT` | `tenant` |
|
|
100
|
+
| Project slug | `--project` | `PACK_PROJECT` | `project` (default `main`) |
|
|
101
|
+
| Deploy token | `--token` | `PACK_TOKEN` | — *(use `octwin login`)* |
|
|
102
|
+
|
|
103
|
+
For **CI**, skip `pack.json`/`login` entirely and pass `PACK_PLATFORM_URL`, `PACK_TENANT`,
|
|
104
|
+
`PACK_PROJECT`, and `PACK_TOKEN` as environment variables.
|
|
105
|
+
|
|
106
|
+
## Authentication
|
|
107
|
+
|
|
108
|
+
You authenticate with a tenant-scoped **deploy token** (prefixed `oct_…`) — not a password and not
|
|
109
|
+
an operator token. Generate it in the Octwin console (**your workspace → API tokens → Generate**).
|
|
110
|
+
It is **least-privilege** (scope `pack:deploy`): it can deploy packs to your tenant but cannot
|
|
111
|
+
manage members, billing, or other tenants, and it is revocable at any time.
|
|
112
|
+
|
|
113
|
+
Add the optional **`media:generate`** scope to let a `--seed` deploy AI-generate seed images
|
|
114
|
+
(for a demo record field like `photo: "generate:<prompt>"`); without it, such fields are seeded as
|
|
115
|
+
text only.
|
|
116
|
+
|
|
117
|
+
## What a pack may contain
|
|
118
|
+
|
|
119
|
+
A pack is **pure declarative data** — `.yaml` / `.yml` / `.md` / `.sql` / `.json` only. Executable
|
|
120
|
+
code (`.ts`/`.js`), HTTP routes, DB clients, and custom primitives are **not** allowed (this is what
|
|
121
|
+
makes an external pack safe to run on a shared platform; the server enforces it on deploy). For
|
|
122
|
+
domain records, use Octwin's first-class storage modules — **XRM** (records with stage pipelines),
|
|
123
|
+
**catalog** (products), or **casework** (tickets) — declared in `xrm.yaml` / `cases.yaml`, so a pack
|
|
124
|
+
needs **no database of its own**.
|
|
125
|
+
|
|
126
|
+
## Links
|
|
127
|
+
|
|
128
|
+
- **npm:** <https://www.npmjs.com/package/octwin-cli>
|
|
129
|
+
- **Command help:** `octwin help`
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
[MIT](./LICENSE) © CEQUENS
|
package/dist/index.js
CHANGED
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
* `npx octwin-cli <cmd>` (published as `octwin-cli`, command `octwin`).
|
|
9
9
|
*
|
|
10
10
|
* octwin init <dir> [--id my-pack] [--description "..."] [--display-name "..."]
|
|
11
|
-
* octwin validate [--dir .]
|
|
11
|
+
* octwin validate [--dir .] [--remote] # --remote → the platform's FULL schema check, all errors at once
|
|
12
12
|
* octwin login --url <platformUrl> --token oct_…
|
|
13
13
|
* octwin whoami [--url <url>] [--tenant <slug>]
|
|
14
14
|
* octwin deploy [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>] [--seed]
|
|
15
15
|
* octwin status [--dir .] # did my deploy land? which version is live?
|
|
16
|
+
* octwin records [entity] [id] # inspect the pack's XRM data (records:read token)
|
|
17
|
+
* octwin logs [conversationId] # list conversations / show one's timeline
|
|
18
|
+
* octwin chat "msg" [--as h] # drive one turn via the web channel + print the reply
|
|
16
19
|
* octwin platform-kb [pull] [--dir .] # pull the platform capability reference for the authoring skill
|
|
17
|
-
* octwin test [--dir .] #
|
|
20
|
+
* octwin test [--dir .] # = validate --remote (the full platform check)
|
|
18
21
|
*
|
|
19
22
|
* Config resolution (deploy): flags > pack.json (in the pack dir) > env
|
|
20
23
|
* (PACK_PLATFORM_URL / PACK_TENANT / PACK_PROJECT / PACK_TOKEN) > saved login.
|
|
@@ -168,11 +171,58 @@ function localValidate(packDir) {
|
|
|
168
171
|
}
|
|
169
172
|
return { id, version, files };
|
|
170
173
|
}
|
|
171
|
-
function cmdValidate(flags) {
|
|
174
|
+
async function cmdValidate(flags) {
|
|
172
175
|
const packDir = resolve(flags.dir ?? '.');
|
|
173
|
-
const { id, version, files } = localValidate(packDir);
|
|
174
|
-
console.log(`✓ ${id}@${version}
|
|
175
|
-
|
|
176
|
+
const { id, version, files } = localValidate(packDir); // offline structural gate first (fast, no server/token)
|
|
177
|
+
console.log(`✓ ${id}@${version} passes the offline structural check (${Object.keys(files).length} files)`);
|
|
178
|
+
if (flags.remote !== true) {
|
|
179
|
+
console.log(' Run `octwin validate --remote` to run the platform\'s FULL manifest + flow-DSL validation');
|
|
180
|
+
console.log(' (all errors at once) before you deploy.');
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Remote: the SAME validation the deploy route runs — manifest `.strict()` +
|
|
184
|
+
// every flow (schema/expression/structure) — returning ALL errors at once.
|
|
185
|
+
const { url, tenant, project, token } = resolveTarget(flags, packDir);
|
|
186
|
+
console.log(`→ Validating against ${tenant}/${project} @ ${url} …`);
|
|
187
|
+
const res = await fetch(`${url}/api/admin/tenants/${tenant}/projects/${project}/packs/validate`, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${token}` },
|
|
190
|
+
body: JSON.stringify({ files }),
|
|
191
|
+
});
|
|
192
|
+
const text = await res.text();
|
|
193
|
+
let json;
|
|
194
|
+
try {
|
|
195
|
+
json = JSON.parse(text);
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
json = text;
|
|
199
|
+
}
|
|
200
|
+
if (!res.ok) {
|
|
201
|
+
// 404 → older platform without the endpoint.
|
|
202
|
+
if (res.status === 404)
|
|
203
|
+
die('this platform has no /packs/validate endpoint yet (older version) — deploy runs the full check');
|
|
204
|
+
console.error(`✗ remote validate failed (HTTP ${res.status})`);
|
|
205
|
+
console.error(typeof json === 'string' ? json : JSON.stringify(json, null, 2));
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
if (json?.ok) {
|
|
209
|
+
console.log(`✓ ${id}@${version} passes the platform's FULL validation — deploy won't reject on schema.`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const errors = (json?.errors ?? []);
|
|
213
|
+
const byWhere = new Map();
|
|
214
|
+
for (const e of errors) {
|
|
215
|
+
const a = byWhere.get(e.where) ?? [];
|
|
216
|
+
a.push(e.message);
|
|
217
|
+
byWhere.set(e.where, a);
|
|
218
|
+
}
|
|
219
|
+
console.error(`✗ ${id}@${version} — ${errors.length} validation error${errors.length === 1 ? '' : 's'}:`);
|
|
220
|
+
for (const [where, msgs] of byWhere) {
|
|
221
|
+
console.error(`\n [${where}]`);
|
|
222
|
+
for (const m of msgs)
|
|
223
|
+
console.error(` • ${m}`);
|
|
224
|
+
}
|
|
225
|
+
process.exit(1);
|
|
176
226
|
}
|
|
177
227
|
function cmdLogin(flags) {
|
|
178
228
|
const url = flags.url ?? process.env.PACK_PLATFORM_URL ?? die('usage: octwin login --url <platformUrl> --token <t>');
|
|
@@ -399,27 +449,216 @@ async function cmdPlatformKb(flags) {
|
|
|
399
449
|
console.log(` ${mdCount} markdown docs + ${jsonCount} JSON catalogs (reference version ${bundle.version ?? '?'})`);
|
|
400
450
|
console.log(' The octwin-pack authoring skill reads these as the source of truth for what the platform supports.');
|
|
401
451
|
}
|
|
402
|
-
|
|
452
|
+
// ── records / logs / chat — headless inspect + test with the deploy token ────
|
|
453
|
+
/** GET an admin endpoint with the deploy token; returns `{ status, json }`. */
|
|
454
|
+
async function apiGet(endpoint, token) {
|
|
455
|
+
const res = await fetch(endpoint, { headers: { authorization: `Bearer ${token}` } });
|
|
456
|
+
const text = await res.text();
|
|
457
|
+
let json;
|
|
458
|
+
try {
|
|
459
|
+
json = JSON.parse(text);
|
|
460
|
+
}
|
|
461
|
+
catch {
|
|
462
|
+
json = text;
|
|
463
|
+
}
|
|
464
|
+
return { status: res.status, json };
|
|
465
|
+
}
|
|
466
|
+
/** `octwin records [entity] [id]` — inspect the pack's XRM data (needs a `records:read` token). */
|
|
467
|
+
async function cmdRecords(flags) {
|
|
403
468
|
const packDir = resolve(flags.dir ?? '.');
|
|
404
|
-
const {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
469
|
+
const { url, tenant, project, token } = resolveTarget(flags, packDir);
|
|
470
|
+
const base = `${url}/api/admin/tenants/${tenant}/projects/${project}`;
|
|
471
|
+
const entity = flags._[0];
|
|
472
|
+
const recordId = flags._[1];
|
|
473
|
+
if (!entity) {
|
|
474
|
+
const { status, json } = await apiGet(`${base}/xrm/entities`, token);
|
|
475
|
+
if (status !== 200)
|
|
476
|
+
die(`could not read entities (HTTP ${status})`);
|
|
477
|
+
if (json?.has_xrm === false) {
|
|
478
|
+
console.log('This pack declares no XRM entities.');
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const ents = (json?.entities ?? []);
|
|
482
|
+
if (ents.length === 0) {
|
|
483
|
+
console.log('No entities visible — mint a token with the `records:read` scope to inspect data.');
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
console.log(`Entities in ${tenant}/${project}:`);
|
|
487
|
+
for (const e of ents)
|
|
488
|
+
console.log(` ${e.entity} (${e.open_count ?? 0} records)`);
|
|
489
|
+
console.log('\nList records: octwin records <entity>');
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (!recordId) {
|
|
493
|
+
const limit = flags.limit ?? '50';
|
|
494
|
+
const { status, json } = await apiGet(`${base}/xrm/records?entity=${encodeURIComponent(entity)}&limit=${limit}`, token);
|
|
495
|
+
if (status === 403)
|
|
496
|
+
die('forbidden — the paged record list needs the `records` plan feature on this tenant');
|
|
497
|
+
if (status !== 200)
|
|
498
|
+
die(`could not read records (HTTP ${status})`);
|
|
499
|
+
const rows = (json?.records ?? []);
|
|
500
|
+
console.log(`${entity}: ${json?.total ?? rows.length} record(s)`);
|
|
501
|
+
if (rows.length === 0)
|
|
502
|
+
console.log(' (none — if you expected data, mint a `records:read` token and check `octwin deploy --seed`)');
|
|
503
|
+
for (const r of rows)
|
|
504
|
+
console.log(` #${r.record_number ?? '?'} ${r.title ?? '(untitled)'}${r.stage ? ` [${r.stage}]` : ''} ${r.id}`);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const { status, json } = await apiGet(`${base}/xrm/records/${encodeURIComponent(recordId)}`, token);
|
|
508
|
+
if (status === 403)
|
|
509
|
+
die('forbidden — mint a token with the `records:read` scope');
|
|
510
|
+
if (status === 404)
|
|
511
|
+
die(`record '${recordId}' not found`);
|
|
512
|
+
if (status !== 200)
|
|
513
|
+
die(`could not read record (HTTP ${status})`);
|
|
514
|
+
console.log(JSON.stringify(json?.record ?? json, null, 2));
|
|
515
|
+
}
|
|
516
|
+
/** `octwin logs [conversationId]` — list conversations or show one's event timeline. */
|
|
517
|
+
async function cmdLogs(flags) {
|
|
518
|
+
const packDir = resolve(flags.dir ?? '.');
|
|
519
|
+
const { url, tenant, project, token } = resolveTarget(flags, packDir);
|
|
520
|
+
const base = `${url}/api/admin/tenants/${tenant}/projects/${project}`;
|
|
521
|
+
const convId = flags._[0];
|
|
522
|
+
if (!convId) {
|
|
523
|
+
const { status, json } = await apiGet(`${base}/conversations?limit=20`, token);
|
|
524
|
+
if (status !== 200)
|
|
525
|
+
die(`could not read conversations (HTTP ${status})`);
|
|
526
|
+
const convs = (json?.conversations ?? []);
|
|
527
|
+
if (convs.length === 0) {
|
|
528
|
+
console.log('No conversations yet — try `octwin chat "hi"` first.');
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
console.log(`Recent conversations in ${tenant}/${project}:`);
|
|
532
|
+
for (const c of convs)
|
|
533
|
+
console.log(` ${c.id} ${c.contact?.display_name ?? c.contact?.channel_contact_handle ?? '?'} [${c.status}]`);
|
|
534
|
+
console.log('\nView a timeline: octwin logs <conversationId>');
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
const { status, json } = await apiGet(`${base}/conversations/${encodeURIComponent(convId)}`, token);
|
|
538
|
+
if (status === 404)
|
|
539
|
+
die(`conversation '${convId}' not found`);
|
|
540
|
+
if (status !== 200)
|
|
541
|
+
die(`could not read conversation (HTTP ${status})`);
|
|
542
|
+
const events = (json?.events ?? []);
|
|
543
|
+
console.log(`Timeline for ${convId} (${events.length} events):`);
|
|
544
|
+
for (const e of events) {
|
|
545
|
+
const isErr = e.type === 'tool' && e.subtype === 'platform_error';
|
|
546
|
+
const tag = isErr ? '⚠ ERROR' : `${e.type}${e.subtype ? `/${e.subtype}` : ''}`;
|
|
547
|
+
const body = typeof e.content === 'string' ? e.content : JSON.stringify(e.content ?? '');
|
|
548
|
+
console.log(` ${e.ts ?? ''} ${tag}: ${(body ?? '').slice(0, 240)}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
/** `octwin chat "message" [--as <handle>]` — drive ONE turn through the dev web
|
|
552
|
+
* channel and print the agent's reply. The web channel is unauthenticated, so no
|
|
553
|
+
* token is required here. */
|
|
554
|
+
async function cmdChat(flags) {
|
|
555
|
+
const packDir = resolve(flags.dir ?? '.');
|
|
556
|
+
const cfg = readPackConfig(packDir);
|
|
557
|
+
const url = (flags.url ?? process.env.PACK_PLATFORM_URL ?? cfg.platform_url ?? '').replace(/\/$/, '');
|
|
558
|
+
const tenant = flags.tenant ?? process.env.PACK_TENANT ?? cfg.tenant ?? '';
|
|
559
|
+
const project = flags.project ?? process.env.PACK_PROJECT ?? cfg.project ?? 'main';
|
|
560
|
+
if (!url)
|
|
561
|
+
die('no platform url — set it in pack.json, --url, or PACK_PLATFORM_URL');
|
|
562
|
+
if (!tenant)
|
|
563
|
+
die('no tenant — set it in pack.json, --tenant, or PACK_TENANT');
|
|
564
|
+
const from = flags.as ?? 'cli-tester';
|
|
565
|
+
const message = flags._[0];
|
|
566
|
+
if (!message)
|
|
567
|
+
die('usage: octwin chat "your message" [--as <handle>]');
|
|
568
|
+
const ctrl = new AbortController();
|
|
569
|
+
const timer = setTimeout(() => ctrl.abort(), 30_000);
|
|
570
|
+
let evRes;
|
|
571
|
+
try {
|
|
572
|
+
evRes = await fetch(`${url}/api/web/events/${tenant}/${project}/${encodeURIComponent(from)}`, { headers: { accept: 'text/event-stream' }, signal: ctrl.signal });
|
|
573
|
+
}
|
|
574
|
+
catch (err) {
|
|
575
|
+
clearTimeout(timer);
|
|
576
|
+
die(`could not open chat stream: ${err?.message ?? err}`);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (!evRes.ok || !evRes.body) {
|
|
580
|
+
clearTimeout(timer);
|
|
581
|
+
die(`could not open chat stream (HTTP ${evRes.status})`);
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
console.log(`→ [${from}] ${message}`);
|
|
585
|
+
await fetch(`${url}/api/web/inbound/${tenant}/${project}`, {
|
|
586
|
+
method: 'POST',
|
|
587
|
+
headers: { 'content-type': 'application/json' },
|
|
588
|
+
body: JSON.stringify({ type: 'text', from, text: message, local_id: `cli-${from}` }),
|
|
589
|
+
}).catch((err) => { clearTimeout(timer); die(`could not send message: ${err?.message ?? err}`); });
|
|
590
|
+
const reader = evRes.body.getReader();
|
|
591
|
+
const decoder = new TextDecoder();
|
|
592
|
+
let buf = '';
|
|
593
|
+
let gotReply = false;
|
|
594
|
+
try {
|
|
595
|
+
outer: for (;;) {
|
|
596
|
+
const { done, value } = await reader.read();
|
|
597
|
+
if (done)
|
|
598
|
+
break;
|
|
599
|
+
buf += decoder.decode(value, { stream: true });
|
|
600
|
+
let idx;
|
|
601
|
+
while ((idx = buf.indexOf('\n\n')) >= 0) {
|
|
602
|
+
const frame = buf.slice(0, idx);
|
|
603
|
+
buf = buf.slice(idx + 2);
|
|
604
|
+
const line = frame.split('\n').find((l) => l.startsWith('data:'));
|
|
605
|
+
if (!line)
|
|
606
|
+
continue;
|
|
607
|
+
let ev;
|
|
608
|
+
try {
|
|
609
|
+
ev = JSON.parse(line.slice(5).trim());
|
|
610
|
+
}
|
|
611
|
+
catch {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (ev.kind === 'render') {
|
|
615
|
+
console.log(`← ${ev.body ?? '(no text body)'}`);
|
|
616
|
+
const hint = ev.hint;
|
|
617
|
+
if (hint?.buttons?.length)
|
|
618
|
+
console.log(` [buttons: ${hint.buttons.map((b) => b.title).join(' | ')}]`);
|
|
619
|
+
if (hint?.sections?.length || hint?.rows?.length)
|
|
620
|
+
console.log(' [a list picker was rendered]');
|
|
621
|
+
gotReply = true;
|
|
622
|
+
break outer;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
catch (err) {
|
|
628
|
+
if (ctrl.signal.aborted)
|
|
629
|
+
console.error(' (timed out after 30s waiting for a reply)');
|
|
630
|
+
else
|
|
631
|
+
console.error(` (stream error: ${err?.message ?? err})`);
|
|
632
|
+
}
|
|
633
|
+
finally {
|
|
634
|
+
clearTimeout(timer);
|
|
635
|
+
try {
|
|
636
|
+
await reader.cancel();
|
|
637
|
+
}
|
|
638
|
+
catch { /* ignore */ }
|
|
639
|
+
}
|
|
640
|
+
if (!gotReply) {
|
|
641
|
+
console.error(' no reply — the pack may not be warm yet, or the turn produced no render.');
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
408
644
|
}
|
|
409
645
|
function help() {
|
|
410
|
-
console.log(`octwin — Octwin external-pack developer CLI (by CEQUENS)
|
|
411
|
-
|
|
412
|
-
octwin init <dir> [--id my-pack] [--description "..."] [--display-name "..."]
|
|
413
|
-
octwin validate [--dir .]
|
|
414
|
-
octwin login --url <platformUrl> --token oct_… # a deploy token from the console
|
|
415
|
-
octwin whoami [--url <url>] [--tenant <slug>] # verify the token works
|
|
416
|
-
octwin deploy [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>] [--seed]
|
|
417
|
-
octwin status [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>]
|
|
418
|
-
octwin
|
|
419
|
-
octwin
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
octwin
|
|
646
|
+
console.log(`octwin — Octwin external-pack developer CLI (by CEQUENS)
|
|
647
|
+
|
|
648
|
+
octwin init <dir> [--id my-pack] [--description "..."] [--display-name "..."]
|
|
649
|
+
octwin validate [--dir .] [--remote] # --remote runs the platform's FULL schema check (all errors at once)
|
|
650
|
+
octwin login --url <platformUrl> --token oct_… # a deploy token from the console
|
|
651
|
+
octwin whoami [--url <url>] [--tenant <slug>] # verify the token works
|
|
652
|
+
octwin deploy [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>] [--seed]
|
|
653
|
+
octwin status [--dir .] [--url <url>] [--tenant <slug>] [--project <slug>] [--token <t>]
|
|
654
|
+
octwin records [entity] [id] # inspect the pack's XRM data (needs a records:read token)
|
|
655
|
+
octwin logs [conversationId] # list conversations / show one's event timeline
|
|
656
|
+
octwin chat "message" [--as <handle>] # drive one turn through the web channel + print the reply
|
|
657
|
+
octwin platform-kb [pull] [--dir .] [--url <url>] [--tenant <slug>] [--token <t>]
|
|
658
|
+
octwin test [--dir .] # = validate --remote (the full platform check)
|
|
659
|
+
|
|
660
|
+
Get a deploy token: console → your workspace → Settings → API tokens → Generate (tick records:read to inspect data).
|
|
661
|
+
octwin platform-kb pull → writes the platform capability reference into .octwin/platform-kb/ (for the octwin-pack skill).
|
|
423
662
|
Config (deploy): flags > pack.json > env (PACK_PLATFORM_URL/PACK_TENANT/PACK_PROJECT/PACK_TOKEN) > saved login.`);
|
|
424
663
|
}
|
|
425
664
|
async function main() {
|
|
@@ -430,7 +669,7 @@ async function main() {
|
|
|
430
669
|
cmdInit(flags);
|
|
431
670
|
break;
|
|
432
671
|
case 'validate':
|
|
433
|
-
cmdValidate(flags);
|
|
672
|
+
await cmdValidate(flags);
|
|
434
673
|
break;
|
|
435
674
|
case 'login':
|
|
436
675
|
cmdLogin(flags);
|
|
@@ -444,12 +683,21 @@ async function main() {
|
|
|
444
683
|
case 'status':
|
|
445
684
|
await cmdStatus(flags);
|
|
446
685
|
break;
|
|
686
|
+
case 'records':
|
|
687
|
+
await cmdRecords(flags);
|
|
688
|
+
break;
|
|
689
|
+
case 'logs':
|
|
690
|
+
await cmdLogs(flags);
|
|
691
|
+
break;
|
|
692
|
+
case 'chat':
|
|
693
|
+
await cmdChat(flags);
|
|
694
|
+
break;
|
|
447
695
|
case 'platform-kb':
|
|
448
696
|
await cmdPlatformKb(flags);
|
|
449
697
|
break;
|
|
450
698
|
case 'test':
|
|
451
|
-
|
|
452
|
-
break;
|
|
699
|
+
await cmdValidate({ ...flags, remote: true });
|
|
700
|
+
break; // A6: `test` = the full remote validate, not a validate-clone
|
|
453
701
|
case undefined:
|
|
454
702
|
case 'help':
|
|
455
703
|
case '--help':
|
package/package.json
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "octwin-cli",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Octwin external-pack developer CLI (by CEQUENS) — scaffold, validate, deploy, and check pure-YAML packs on your tenant.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"octwin": "dist/index.js"
|
|
8
|
-
},
|
|
9
|
-
"files": [
|
|
10
|
-
"dist",
|
|
11
|
-
"templates",
|
|
12
|
-
"README.md",
|
|
13
|
-
"LICENSE"
|
|
14
|
-
],
|
|
15
|
-
"engines": {
|
|
16
|
-
"node": ">=20"
|
|
17
|
-
},
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsc -p tsconfig.json",
|
|
20
|
-
"prepublishOnly": "npm run build"
|
|
21
|
-
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"yaml": "^2.6.0"
|
|
24
|
-
},
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"@types/node": "^22.0.0",
|
|
27
|
-
"typescript": "^5.7.0"
|
|
28
|
-
},
|
|
29
|
-
"publishConfig": {
|
|
30
|
-
"access": "public"
|
|
31
|
-
},
|
|
32
|
-
"keywords": ["octwin", "cequens", "cli", "whatsapp", "chatbot", "pack", "conversational-ai", "yaml"],
|
|
33
|
-
"author": "CEQUENS",
|
|
34
|
-
"homepage": "https://www.npmjs.com/package/octwin-cli",
|
|
35
|
-
"license": "MIT"
|
|
36
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "octwin-cli",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Octwin external-pack developer CLI (by CEQUENS) — scaffold, validate, deploy, and check pure-YAML packs on your tenant.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"octwin": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"templates",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"yaml": "^2.6.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^22.0.0",
|
|
27
|
+
"typescript": "^5.7.0"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"keywords": ["octwin", "cequens", "cli", "whatsapp", "chatbot", "pack", "conversational-ai", "yaml"],
|
|
33
|
+
"author": "CEQUENS",
|
|
34
|
+
"homepage": "https://www.npmjs.com/package/octwin-cli",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Pack-level slash-command declarations (OPTIONAL but recommended).
|
|
2
|
-
#
|
|
3
|
-
# The platform's default `systemCommand` matches inbound text against these
|
|
4
|
-
# BEFORE normal agent dispatch. Without a `commands.yaml`, a pack has NO slash
|
|
5
|
-
# commands — e.g. `/clear` silently does nothing (it just goes to the agent as
|
|
6
|
-
# text). Ship this file if you want `/clear` (and friends) to work like the
|
|
7
|
-
# other packs.
|
|
8
|
-
#
|
|
9
|
-
# action: `close_conversation` (reset the conversation) | `noop` (reply only).
|
|
10
|
-
|
|
11
|
-
- match: '/clear'
|
|
12
|
-
action: close_conversation
|
|
13
|
-
reply: 'تم مسح المحادثة ✅ تقدر تبدأ من جديد.'
|
|
14
|
-
source: '/clear'
|
|
1
|
+
# Pack-level slash-command declarations (OPTIONAL but recommended).
|
|
2
|
+
#
|
|
3
|
+
# The platform's default `systemCommand` matches inbound text against these
|
|
4
|
+
# BEFORE normal agent dispatch. Without a `commands.yaml`, a pack has NO slash
|
|
5
|
+
# commands — e.g. `/clear` silently does nothing (it just goes to the agent as
|
|
6
|
+
# text). Ship this file if you want `/clear` (and friends) to work like the
|
|
7
|
+
# other packs.
|
|
8
|
+
#
|
|
9
|
+
# action: `close_conversation` (reset the conversation) | `noop` (reply only).
|
|
10
|
+
|
|
11
|
+
- match: '/clear'
|
|
12
|
+
action: close_conversation
|
|
13
|
+
reply: 'تم مسح المحادثة ✅ تقدر تبدأ من جديد.'
|
|
14
|
+
source: '/clear'
|