newo 3.7.3 → 3.7.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/CHANGELOG.md CHANGED
@@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [3.7.5] - 2026-06-17
11
+
12
+ ### Added
13
+
14
+ - **`newo sandbox` connector selection (R1)** — new `--connector <connector_idn>` and `--integration <idn>` flags select which connector to chat through instead of always taking the first running connector of the `sandbox` integration (which made agents like Vibe Builder's `vibe_agent` unreachable from the CLI when another connector, e.g. `convo_agent_sandbox`, came first). `--list-connectors` prints the running connectors (with `--json` for machine output). When `--connector` is not found, the error lists the available running connectors. Without flags, behavior is unchanged (first running connector). Surfaced during PR #2076 superagent testing.
15
+ - **`newo sandbox` long messages and machine-readable output (R2)** — `--file <path>` and `--stdin` send message text without shell-argument limits (tested scenarios used 40k–400k char messages); `--timeout <seconds>` overrides the previously hardcoded 60-second response-poll limit (long VibeFlow turns ran 1–7 minutes); `--json` emits `{actor_id, persona_id, connector_idn, external_event_id, user_external_event_id, agent_external_event_id, response, elapsed_ms, timed_out, flow_idn, skill_idn, session_id}`. The `external_event_id` of the user turn is the correlation key for `newo logs --event-id`, closing the gap that previously required ad-hoc scripts importing `dist/api.js` directly.
16
+ - **`newo get-skill` / `newo update-skill` (R3)** — point inspection and point edits of a single skill on the platform by IDN path (`<skill-idn> --project <idn> --agent <idn> --flow <idn>`), with no pulled workspace required. `get-skill` shows the live model, runner_type, parameters, and prompt_script (`--json` supported). `update-skill` changes `--model <provider_idn>/<model_idn>` and/or `--script <file>`, leaving everything else untouched; `--publish` (with optional `--publish-description`) publishes the flow afterwards, otherwise changes stay draft. Warns when a local pulled workspace exists and now diverges. Covers the "switch model → run test → switch back" scenario in two commands.
17
+ - **`newo logs --name <ActionName>` (R4)** — client-side filter on `data.name` (e.g. only `Gen` or `GetMemory` action calls), replacing `--json` + jq post-filtering. Help text now documents that the model of a turn must be read from `data.source.model` in `--json` output, not inferred from actor/agent names.
18
+ - **`newo logs --raw`** — JSONL output: one JSON object per log line (sorted oldest-first), with no banners or empty-result text, so stdout is a clean machine-readable stream for piping (`newo logs --raw | jq ...`). Distinct from `--json`, which emits a single pretty-printed JSON array.
19
+ - **`src/sync/remote-skill.ts`** — shared resolver (`resolveRemoteSkill`, `parseModelFlag`) that maps a project/agent/flow/skill IDN path to platform IDs with descriptive errors listing available IDNs at the failing level.
20
+ - **20 unit tests** — 14 in `test/sandbox-skill-commands.test.js` covering connector selection (legacy first-running behavior, exact match, non-running exclusion, error listings), poll timeout override, user/agent `external_event_id` capture, IDN-path resolution, and `--model` flag parsing, plus 6 in `test/logs-command.test.js` covering `--name` client-side filtering, pagination, and `--raw` JSONL output — all against a fake axios client, no network.
21
+
22
+ ## [3.7.4] - 2026-06-10
23
+
24
+ ### Added
25
+
26
+ - **V2 push now creates new skills from inline flow YAML definitions.** Previously the `newo_v2` push path could only update content of skills already known to the local project map; a skill added locally to `{FlowIdn}.yaml` after the last pull was silently ignored, leaving its `.nsl`/`.nslg` script with no way to reach the platform. `V2ProjectSyncStrategy.push()` now reconciles inline skill metadata before iterating script changes: missing remote skills are created via `POST /api/v1/designer/flows/{flowId}/skills`, race-condition duplicates (already-exists on create) fall back to fetching the remote skill via `listFlowSkills` and updating it, and missing skill parameters are filled in via `POST /api/v1/designer/flows/skills/{skillId}/parameters`. The local project map and SHA256 hash store are updated atomically after the reconciliation pass.
27
+ - **Strict model validation before V2 skill writes.** New `assertSkillModelResolved` helper throws a descriptive error ("Set either skill.model.* or flow default_model_idn/default_provider_idn") when neither the inline skill nor the flow declares a model/provider, instead of letting the platform return a generic 4xx with no hint about which YAML field is missing.
28
+ - **33 unit tests** in `test/v2-push-helpers.test.js` covering `isAlreadyExistsApiError`, `assertSkillModelResolved`, `normalizeRunnerType`, `normalizeParameters`, `skillMetadataDiffers`, `buildV2SkillMetadataFromYaml`, and `createMissingSkillParameters` (stubbed API client). Regression-locks the false-positive matcher fix, the model-validation contract, the parameter-creation counting, and the model-key-order false positive.
29
+
30
+ ### Fixed
31
+
32
+ - **`isAlreadyExistsApiError` no longer matches "does not exist".** The previous detector accepted any 400/409/422 response whose body contained the substring `"exist"`, which incorrectly classified "Skill does not exist" / "Flow doesn't exist" errors as duplicates and triggered a spurious reuse fallback. The matcher is now tightened to only `"already exists"` and `"duplicate key"`.
33
+ - **`validate()` reports missing scripts for YAML-declared skills.** Previously `V2ProjectSyncStrategy.validate()` only checked scripts listed in the local map; skills added directly to `{FlowIdn}.yaml` since the last pull were never validated. Validation now walks the YAML when present and surfaces a `Script file not found:` error per skill, matching the new push contract.
34
+ - **Missing-script skills are reported, not silently skipped.** Instead of a `verbose`-only "Skipping skill without local script" log, the push now records `[newo_v2] Missing script for skill {project}/{agent}/{flow}/{skill}: {path}` in the push errors. Failures are isolated per skill: one broken skill no longer aborts the push of every other flow/project in the workspace.
35
+ - **Model validation only runs before actual platform writes.** `assertSkillModelResolved` fires only when a skill is about to be created or its metadata updated — a pre-existing flow YAML without `skill.model.*` / flow `default_model_idn` no longer blocks pushes of unrelated, untouched skills.
36
+ - **Parameter-creation count is no longer inflated by already-existing parameters.** `createMissingSkillParameters` incremented its counter (and logged "Created skill parameter") even when the platform answered "already exists", which spuriously triggered a follow-up `updateSkill` call. Count and log now happen only on successful creation.
37
+ - **`isAlreadyExistsApiError` no longer throws on `null`/`undefined` errors** (optional chaining on `.response`), which previously masked the original failure inside catch handlers.
38
+ - **`skillMetadataDiffers` no longer flags every skill as changed.** The model comparison used `JSON.stringify`, which is key-order-sensitive: the project map stores `{provider_idn, model_idn}` (platform API order) while YAML-built metadata uses `{model_idn, provider_idn}`. Verified against a live account: every push rewrote all 1342 skills. Model is now compared field-by-field and parameters are compared order-insensitively (sorted by name).
39
+ - **Skill parameters are now created explicitly after `createSkill`.** The platform's create endpoint ignores inline `parameters` in the request body (verified against the live platform) — newly created skills silently lost their YAML-declared parameters. The create path now calls `POST /flows/skills/{skillId}/parameters` per parameter, same as the update path.
40
+
10
41
  ## [3.7.3] - 2026-05-25
11
42
 
12
43
  ### Fixed
@@ -1061,7 +1092,9 @@ Another Item: $Price [Modifiers: modifier3]
1061
1092
  - GitHub Actions CI/CD integration
1062
1093
  - Robust authentication with token refresh
1063
1094
 
1064
- [Unreleased]: https://github.com/sabbah13/newo-cli/compare/v3.7.3...HEAD
1095
+ [Unreleased]: https://github.com/sabbah13/newo-cli/compare/v3.7.5...HEAD
1096
+ [3.7.5]: https://github.com/sabbah13/newo-cli/compare/v3.7.4...v3.7.5
1097
+ [3.7.4]: https://github.com/sabbah13/newo-cli/compare/v3.7.3...v3.7.4
1065
1098
  [3.7.3]: https://github.com/sabbah13/newo-cli/compare/v3.7.2...v3.7.3
1066
1099
  [3.7.2]: https://github.com/sabbah13/newo-cli/compare/v3.7.1...v3.7.2
1067
1100
  [3.7.1]: https://github.com/sabbah13/newo-cli/compare/v3.7.0...v3.7.1
@@ -1095,4 +1128,4 @@ Another Item: $Price [Modifiers: modifier3]
1095
1128
  [1.4.0]: https://github.com/sabbah13/newo-cli/compare/v1.2.1...v1.4.0
1096
1129
  [1.3.0]: https://github.com/sabbah13/newo-cli/compare/v1.2.1...v1.3.0
1097
1130
  [1.2.2]: https://github.com/sabbah13/newo-cli/compare/v1.2.1...v1.2.2
1098
- [1.2.1]: https://github.com/sabbah13/newo-cli/releases/tag/v1.2.1
1131
+ [1.2.1]: https://github.com/sabbah13/newo-cli/releases/tag/v1.2.1
package/README.md CHANGED
@@ -8,6 +8,9 @@
8
8
  **NEWO CLI** - Professional command-line tool for NEWO AI Agent development. Features **modular architecture**, **IDN-based file management**, and **comprehensive multi-customer support**.
9
9
 
10
10
  Sync NEWO "Project → Agent → Flow → Skills" structure to local files with:
11
+ - 🆕 **V2 skill creation on push** (v3.7.4) - adding a skill inline to a `newo_v2` `{FlowIdn}.yaml` and pushing now creates it on the platform (previously only updates of existing skills worked)
12
+ - 🆕 **Sandbox connector selection + automation** (v3.7.5) - `newo sandbox --connector <idn>`, `--list-connectors`, `--file`/`--stdin` for long messages, `--timeout`, and `--json` with `external_event_id` for log correlation
13
+ - 🆕 **Point skill edits** (v3.7.5) - `newo get-skill` / `newo update-skill` inspect and modify a single skill (model, script) on the platform without a pulled workspace, with optional `--publish`
11
14
  - 🆕 **Canvas blank-screen hardening** (v3.7.3) - JSON-typed attributes (e.g. Workflow Builder canvas) with Markdown `\_` escapes or structural newlines no longer corrupt the canvas on push ([#7](https://github.com/sabbah13/newo-cli/pull/7))
12
15
  - 🆕 **Flow metadata sync** (v3.7.2) - `newo push` now reconciles flow title, events, and state_fields from local `metadata.yaml` to the platform (closes [#3](https://github.com/sabbah13/newo-cli/issues/3))
13
16
  - 🆕 **Dual format support** (v3.6.0) - `cli_v1` (native) and `newo_v2` (platform compatible), auto-detected per customer
@@ -149,12 +152,78 @@ NEWO_REFRESH_URL=custom_refresh_endpoint # Custom refresh endpoint
149
152
  | `newo push [--format <fmt>]` | Upload local changes to NEWO | • Works with both formats<br>• Hash-based change detection<br>• Library skill updates<br>• Publishes flows automatically |
150
153
  | `newo status [--format <fmt>]` | Show modified files | • Format-aware status<br>• Multiple file warnings<br>• Per-customer status |
151
154
  | `newo export [--output <file>]` | Download V2 bulk ZIP from platform | • Complete organization export<br>• Projects, agents, flows, skills, attributes, AKB<br>• Compatible with platform UI import |
152
- | `newo sandbox` | Test agents in sandbox chat mode | • Single-command mode for automation<br>• Multi-turn conversation support<br>• Debug info for agent development |
155
+ | `newo sandbox` | Test agents in sandbox chat mode | • Single-command mode for automation<br>• Multi-turn conversation support<br>• Debug info for agent development<br>• v3.7.5: `--connector`, `--list-connectors`, `--file`/`--stdin`, `--timeout`, `--json` |
156
+ | `newo get-skill` / `newo update-skill` | Inspect / point-edit one skill on the platform (NEW v3.7.5) | • No pulled workspace required<br>• `--model <provider>/<model>`, `--script <file>`<br>• Optional `--publish` |
153
157
  | `newo conversations` | Pull conversation history | • User personas and chat history<br>• YAML format output<br>• Pagination support |
154
158
  | `newo list-customers` | List configured customers | • Shows default customer<br>• Multi-customer discovery |
155
159
  | `newo import-akb` | Import knowledge base articles | • Structured text parsing<br>• Bulk article import<br>• Validation and error reporting |
156
160
  | `newo meta` | Get project metadata (debug) | • Project structure analysis<br>• Metadata validation |
157
161
 
162
+ ### Sandbox Connector Selection & Automation (NEW v3.7.5)
163
+
164
+ `newo sandbox` previously always chatted through the **first** running connector of the `sandbox` integration, making other agents (e.g. a Vibe Builder behind a `vibe_agent` connector) unreachable from the CLI. v3.7.5 adds connector selection plus automation-friendly I/O:
165
+
166
+ ```bash
167
+ newo sandbox --list-connectors # show running sandbox connectors
168
+ newo sandbox "ping" --connector vibe_agent # chat through a specific connector
169
+ newo sandbox "ping" --integration sandbox --connector vibe_agent # explicit pair (default integration: sandbox)
170
+
171
+ newo sandbox --file ./chunk1.txt --actor <id> --json # long message from file, machine-readable output
172
+ cat chunk1.txt | newo sandbox --stdin --actor <id> # message from stdin
173
+ newo sandbox "ping" --timeout 420 # wait up to 7 minutes for slow agent turns (default: 60s)
174
+ ```
175
+
176
+ `--json` output includes the correlation keys for `newo logs`:
177
+
178
+ ```json
179
+ {
180
+ "actor_id": "…", "persona_id": "…", "connector_idn": "vibe_agent",
181
+ "external_event_id": "…", // user turn — use with: newo logs --event-id <id>
182
+ "user_external_event_id": "…", "agent_external_event_id": "…",
183
+ "response": "…", "elapsed_ms": 12345, "timed_out": false,
184
+ "flow_idn": "VibeFlow", "skill_idn": "…", "session_id": "…"
185
+ }
186
+ ```
187
+
188
+ Without `--connector`, behavior is unchanged (first running connector), so existing scripts keep working. When the requested connector is missing, the error lists the available running connectors.
189
+
190
+ This makes agent integration tests scriptable in plain bash: send chunks via `newo sandbox --file … --json`, then verify behavior via `newo logs --event-id … --json` — no more ad-hoc Node scripts importing `dist/api.js`.
191
+
192
+ ### Point Skill Inspection & Edits (NEW v3.7.5)
193
+
194
+ Inspect or modify a **single skill** directly on the platform by IDN path — no pulled `newo_customers/` workspace required, nothing else gets touched:
195
+
196
+ ```bash
197
+ # What lives on the platform right now? (model, runner_type, parameters, prompt_script)
198
+ newo get-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow [--json]
199
+
200
+ # Temporarily switch a model, publish, run tests, switch back:
201
+ newo update-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow \
202
+ --model google/gemini25_pro --publish
203
+ # …run your tests…
204
+ newo update-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow \
205
+ --model openai/gpt54 --publish
206
+
207
+ # Replace a skill script from a file (draft only, without --publish):
208
+ newo update-skill get_memory --project vibe --agent VibeAgent --flow VibeFlow --script ./patched.nsl
209
+ ```
210
+
211
+ - `--model <provider_idn>/<model_idn>` and `--script <file>` can be combined; everything else (title, parameters, runner) is preserved.
212
+ - `--publish` publishes the flow after the update (same as push); without it the change stays draft. `--publish-description "<text>"` sets the publish note.
213
+ - If a pulled local workspace exists for the project, the CLI warns that it now diverges from the platform.
214
+
215
+ ### Logs: Action-Name Filter (NEW v3.7.5)
216
+
217
+ ```bash
218
+ newo logs --type call --name Gen --json # only Gen action calls
219
+ newo logs --name GetMemory --hours 24 # only GetMemory calls, last 24h
220
+ newo logs --type call --raw | jq '.data.name' # JSONL stream, one log per line, for piping
221
+ ```
222
+
223
+ `--name` filters by `data.name` client-side (the API has no such query param). Note: the **model used for a turn** is in `data.source.model` of the `--json` output — do not infer it from actor/agent names.
224
+
225
+ `--raw` emits **JSONL** — one JSON object per log line (oldest-first), with no banners or "no logs found" text — so stdout is a clean stream for `jq`/piping. This differs from `--json`, which prints a single pretty-printed JSON array.
226
+
158
227
  ### Flow Metadata Sync (NEW v3.7.2)
159
228
 
160
229
  `newo push` now reconciles **flow-level metadata** — title, `events:`, and `state_fields:` — from local YAML to the platform. Before v3.7.2 push only uploaded skill scripts, so edits to a flow's `metadata.yaml` (V1) or `{FlowIdn}.yaml` (V2) silently never reached the platform; events added via `newo create-event` could appear to disappear after a pull → push cycle. Closes [#3](https://github.com/sabbah13/newo-cli/issues/3).
@@ -1378,4 +1447,4 @@ NEWO CLI integrates with these NEWO platform endpoints:
1378
1447
 
1379
1448
  ---
1380
1449
 
1381
- **Built with ❤️ by the NEWO team**
1450
+ **Built with ❤️ by the NEWO team**
@@ -0,0 +1,3 @@
1
+ import type { MultiCustomerConfig, CliArgs } from '../../types.js';
2
+ export declare function handleGetSkillCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose?: boolean): Promise<void>;
3
+ //# sourceMappingURL=get-skill.d.ts.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Get Skill Command Handler - Inspect a skill's live state on the platform
3
+ *
4
+ * Usage:
5
+ * newo get-skill <skill-idn> --project <idn> --agent <idn> --flow <idn> [--json]
6
+ *
7
+ * Shows the skill as it currently lives on the platform (model, runner_type,
8
+ * parameters, prompt_script) without requiring a pulled local workspace.
9
+ */
10
+ import { requireSingleCustomer } from '../customer-selection.js';
11
+ import { makeClient } from '../../api.js';
12
+ import { getValidAccessToken } from '../../auth.js';
13
+ import { resolveRemoteSkill } from '../../sync/remote-skill.js';
14
+ const USAGE = 'Usage: newo get-skill <skill-idn> --project <project-idn> --agent <agent-idn> --flow <flow-idn> [--json] [--customer <idn>]';
15
+ export async function handleGetSkillCommand(customerConfig, args, verbose = false) {
16
+ const skillIdn = args._[1];
17
+ const projectIdn = args.project;
18
+ const agentIdn = args.agent;
19
+ const flowIdn = args.flow;
20
+ const asJson = Boolean(args.json);
21
+ if (!skillIdn || !projectIdn || !agentIdn || !flowIdn) {
22
+ console.error('Error: skill IDN, --project, --agent and --flow are required');
23
+ console.error(USAGE);
24
+ process.exit(1);
25
+ }
26
+ const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
27
+ if (asJson) {
28
+ process.env.NEWO_QUIET_MODE = 'true'; // keep stdout machine-readable for piping
29
+ }
30
+ const token = await getValidAccessToken(selectedCustomer);
31
+ const client = await makeClient(verbose, token);
32
+ if (verbose)
33
+ console.log(`🔍 Resolving skill ${projectIdn}/${agentIdn}/${flowIdn}/${skillIdn}...`);
34
+ const { project, agent, flow, skill } = await resolveRemoteSkill(client, {
35
+ projectIdn,
36
+ agentIdn,
37
+ flowIdn,
38
+ skillIdn
39
+ });
40
+ if (asJson) {
41
+ console.log(JSON.stringify({
42
+ project_idn: project.idn,
43
+ agent_idn: agent.idn,
44
+ flow_idn: flow.idn,
45
+ id: skill.id,
46
+ idn: skill.idn,
47
+ title: skill.title,
48
+ runner_type: skill.runner_type,
49
+ model: skill.model,
50
+ parameters: skill.parameters,
51
+ prompt_script: skill.prompt_script ?? null
52
+ }, null, 2));
53
+ return;
54
+ }
55
+ console.log(`📜 Skill: ${project.idn}/${agent.idn}/${flow.idn}/${skill.idn}`);
56
+ console.log(` ID: ${skill.id}`);
57
+ console.log(` Title: ${skill.title}`);
58
+ console.log(` Runner type: ${skill.runner_type}`);
59
+ console.log(` Model: ${skill.model.provider_idn}/${skill.model.model_idn}`);
60
+ if (skill.parameters.length > 0) {
61
+ console.log(` Parameters:`);
62
+ for (const param of skill.parameters) {
63
+ console.log(` ${param.name}${param.default_value !== undefined ? ` = ${JSON.stringify(param.default_value)}` : ''}`);
64
+ }
65
+ }
66
+ else {
67
+ console.log(` Parameters: (none)`);
68
+ }
69
+ console.log(`\n--- prompt_script (${(skill.prompt_script || '').length} chars) ---`);
70
+ console.log(skill.prompt_script || '(empty)');
71
+ }
72
+ //# sourceMappingURL=get-skill.js.map
@@ -16,6 +16,11 @@ Core Commands:
16
16
  newo conversations [--customer <idn>] [--all] # download user conversations -> conversations.yaml
17
17
  newo sandbox "<message>" [--customer <idn>] # test agent in sandbox - single message mode
18
18
  newo sandbox --actor <id> "message" # continue existing sandbox conversation
19
+ newo sandbox --list-connectors # list running sandbox connectors (NEW v3.7.5)
20
+ newo sandbox "<msg>" --connector <idn> # chat through a specific connector (NEW v3.7.5)
21
+ newo sandbox --file <path> | --stdin # send long messages from file/stdin (NEW v3.7.5)
22
+ newo get-skill <idn> --project <p> --agent <a> --flow <f> [--json] # inspect live skill on platform (NEW v3.7.5)
23
+ newo update-skill <idn> --project <p> --agent <a> --flow <f> [--model <prov>/<model>] [--script <file>] [--publish] # point-edit skill (NEW v3.7.5)
19
24
  newo pull-attributes [--customer <idn>] # download customer + project attributes
20
25
  newo list-customers # list available customers
21
26
  newo meta [--customer <idn>] # get project metadata (debug)
@@ -76,6 +81,8 @@ Analytics & Monitoring (NEW):
76
81
  newo logs --type <types> # filter by type: system, operation, call (comma-separated)
77
82
  newo logs --flow <idn> --skill <idn> # filter by flow and/or skill
78
83
  newo logs --message <text> # search in log messages
84
+ newo logs --name <ActionName> # filter by action name in data.name, e.g. Gen, GetMemory (NEW v3.7.5)
85
+ newo logs --event-id <uuid> # filter by external event ID (correlate with sandbox --json)
79
86
  newo logs --follow, -f # tail mode - continuously poll for new logs
80
87
  newo logs --json # output logs as JSON array
81
88
  newo logs --raw # output each log as single JSON line (for piping)
@@ -94,6 +101,12 @@ Flags:
94
101
  --verbose, -v # enable detailed logging and progress information
95
102
  --quiet, -q # minimal output for automation (sandbox only)
96
103
  --actor <id> # continue existing sandbox chat with actor/chat ID
104
+ --connector <idn> # sandbox: select connector by connector_idn (default: first running)
105
+ --integration <idn> # sandbox: integration to search connectors in (default: sandbox)
106
+ --file <path> # sandbox: read message text from file
107
+ --stdin # sandbox: read message text from stdin
108
+ --timeout <seconds> # sandbox: max wait for agent response (default: 60)
109
+ --json # sandbox: machine-readable output incl. external_event_id (user+agent turns)
97
110
  --confirm # confirm destructive operations without prompting
98
111
  --no-publish # skip automatic flow publishing during push operations
99
112
  --output, -o <file> # output file path (for export command)
@@ -201,6 +214,22 @@ Usage Examples:
201
214
  newo sandbox "Test query" --verbose # With debug info
202
215
  newo sandbox "Test query" --quiet # For automation/scripts
203
216
 
217
+ # Sandbox connector selection + automation (NEW v3.7.5):
218
+ newo sandbox --list-connectors # Show running sandbox connectors
219
+ newo sandbox "ping" --connector vibe_agent # Chat through specific connector
220
+ newo sandbox --file ./chunk1.txt --actor abc123... --json # Long message from file, JSON output
221
+ cat msg.txt | newo sandbox --stdin --timeout 420 --json # From stdin with 7-minute timeout
222
+ # --json output: {actor_id, persona_id, external_event_id, user_external_event_id,
223
+ # agent_external_event_id, response, elapsed_ms, timed_out, ...}
224
+ # Correlate a turn with its logs: newo logs --event-id <external_event_id> --json
225
+
226
+ # Live skill inspection / point edits (NEW v3.7.5):
227
+ newo get-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow # View live state
228
+ newo get-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow --json # As JSON
229
+ newo update-skill structured_generation --project vibe --agent VibeAgent --flow VibeFlow \\
230
+ --model openai/gpt54 --publish # Switch model and publish
231
+ newo update-skill my_skill --project p --agent a --flow f --script ./patched.nsl --publish # Replace script
232
+
204
233
  # Analytics logs (NEW v3.5.0):
205
234
  newo logs # Last 1 hour of logs
206
235
  newo logs --hours 24 # Last 24 hours
@@ -12,7 +12,12 @@
12
12
  * newo logs --follow # Tail mode (poll for new logs)
13
13
  * newo logs --json # Output as JSON
14
14
  */
15
- import type { MultiCustomerConfig, CliArgs } from '../../types.js';
15
+ import type { AxiosInstance } from 'axios';
16
+ import type { MultiCustomerConfig, LogEntry, LogsQueryParams, LogsResponse, CliArgs } from '../../types.js';
17
+ type GetLogsFn = (client: AxiosInstance, params: LogsQueryParams) => Promise<LogsResponse>;
16
18
  export declare function handleLogsCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose: boolean): Promise<void>;
19
+ export declare function collectLogsForDisplay(client: AxiosInstance, params: LogsQueryParams, nameFilter?: string | null, getLogsFn?: GetLogsFn): Promise<LogEntry[]>;
20
+ export declare function fetchAndDisplayLogs(client: AxiosInstance, params: LogsQueryParams, asJson: boolean, raw: boolean, nameFilter?: string | null, getLogsFn?: GetLogsFn): Promise<void>;
17
21
  export declare function printLogsHelp(): void;
22
+ export {};
18
23
  //# sourceMappingURL=logs.d.ts.map
@@ -85,7 +85,14 @@ function formatLogEntryCompact(log, showColors = true) {
85
85
  export async function handleLogsCommand(customerConfig, args, verbose) {
86
86
  // Select customer
87
87
  const selectedCustomer = requireSingleCustomer(customerConfig, args.customer);
88
- console.log(`📊 Fetching logs for ${selectedCustomer.idn}...`);
88
+ // Keep stdout machine-readable when JSON output is requested (for piping to jq etc.)
89
+ const machineOutput = Boolean(args.json || args.raw);
90
+ if (machineOutput) {
91
+ process.env.NEWO_QUIET_MODE = 'true'; // suppress auth logging on stdout
92
+ }
93
+ else {
94
+ console.log(`📊 Fetching logs for ${selectedCustomer.idn}...`);
95
+ }
89
96
  // Get access token and create client
90
97
  const token = await getValidAccessToken(selectedCustomer);
91
98
  const client = await makeClient(verbose, token);
@@ -146,21 +153,59 @@ export async function handleLogsCommand(customerConfig, args, verbose) {
146
153
  const follow = Boolean(args.follow || args.f);
147
154
  const asJson = Boolean(args.json);
148
155
  const raw = Boolean(args.raw);
156
+ // --name filters by data.name (e.g. action name like Gen or GetMemory).
157
+ // The API has no such query param, so it is applied client-side.
158
+ const nameFilter = args.name ? String(args.name) : null;
149
159
  if (follow) {
150
- await tailLogs(client, params, asJson);
160
+ await tailLogs(client, params, asJson, nameFilter);
151
161
  }
152
162
  else {
153
- await fetchAndDisplayLogs(client, params, asJson, raw);
163
+ await fetchAndDisplayLogs(client, params, asJson, raw, nameFilter);
164
+ }
165
+ }
166
+ function filterByName(logs, nameFilter) {
167
+ if (!nameFilter)
168
+ return [...logs];
169
+ return logs.filter(log => log.data['name'] === nameFilter);
170
+ }
171
+ export async function collectLogsForDisplay(client, params, nameFilter = null, getLogsFn = getLogs) {
172
+ if (!nameFilter) {
173
+ const response = await getLogsFn(client, params);
174
+ return [...response.items];
175
+ }
176
+ const pageSize = Number.isFinite(params.per) && params.per && params.per > 0 ? params.per : 50;
177
+ let page = Number.isFinite(params.page) && params.page && params.page > 0 ? params.page : 1;
178
+ const logs = [];
179
+ while (true) {
180
+ const response = await getLogsFn(client, {
181
+ ...params,
182
+ page,
183
+ per: pageSize
184
+ });
185
+ logs.push(...filterByName(response.items, nameFilter));
186
+ if (response.items.length < pageSize) {
187
+ break;
188
+ }
189
+ page++;
154
190
  }
191
+ return logs;
155
192
  }
156
- async function fetchAndDisplayLogs(client, params, asJson, raw) {
193
+ export async function fetchAndDisplayLogs(client, params, asJson, raw, nameFilter = null, getLogsFn = getLogs) {
157
194
  try {
158
- const response = await getLogs(client, params);
159
- const logs = response.items;
195
+ const logs = await collectLogsForDisplay(client, params, nameFilter, getLogsFn);
160
196
  if (asJson) {
161
197
  console.log(JSON.stringify(logs, null, 2));
162
198
  return;
163
199
  }
200
+ // --raw is a machine-readable JSONL contract: stdout must contain only
201
+ // one JSON object per log line, with no banners or empty-result text.
202
+ if (raw) {
203
+ const sortedLogs = [...logs].sort((a, b) => new Date(a.datetime).getTime() - new Date(b.datetime).getTime());
204
+ for (const log of sortedLogs) {
205
+ console.log(JSON.stringify(log));
206
+ }
207
+ return;
208
+ }
164
209
  if (logs.length === 0) {
165
210
  console.log('\nNo logs found for the specified criteria.');
166
211
  return;
@@ -171,12 +216,7 @@ async function fetchAndDisplayLogs(client, params, asJson, raw) {
171
216
  // Detect if stdout is a TTY (supports colors)
172
217
  const useColors = process.stdout.isTTY !== false;
173
218
  for (const log of sortedLogs) {
174
- if (raw) {
175
- console.log(JSON.stringify(log));
176
- }
177
- else {
178
- console.log(formatLogEntry(log, useColors));
179
- }
219
+ console.log(formatLogEntry(log, useColors));
180
220
  }
181
221
  console.log(`\n✅ Displayed ${logs.length} log entries`);
182
222
  }
@@ -185,7 +225,7 @@ async function fetchAndDisplayLogs(client, params, asJson, raw) {
185
225
  console.error('Failed to fetch logs:', err.response?.status, err.response?.data || err.message);
186
226
  }
187
227
  }
188
- async function tailLogs(client, params, asJson) {
228
+ async function tailLogs(client, params, asJson, nameFilter = null) {
189
229
  console.log('🔄 Watching for new logs (Ctrl+C to stop)...\n');
190
230
  const seenLogIds = new Set();
191
231
  let lastCheckTime = params.from_datetime || new Date(Date.now() - 60 * 60 * 1000).toISOString();
@@ -201,7 +241,7 @@ async function tailLogs(client, params, asJson) {
201
241
  per: 100
202
242
  };
203
243
  const response = await getLogs(client, pollParams);
204
- const logs = response.items;
244
+ const logs = filterByName(response.items, nameFilter);
205
245
  // Filter out already seen logs and sort by time
206
246
  const newLogs = logs
207
247
  .filter(log => !seenLogIds.has(log.log_id))
@@ -257,6 +297,7 @@ Filter Options:
257
297
  --flow <idn> Filter by flow IDN
258
298
  --skill <idn> Filter by skill IDN
259
299
  --message <text> Search in log messages
300
+ --name <ActionName> Filter by action name in data.name, e.g. Gen, GetMemory (client-side)
260
301
  --event-id <uuid> Filter by external event ID
261
302
  --runtime-id <uuid> Filter by runtime context ID
262
303
  --actor-id <uuid> Filter by user actor ID
@@ -278,6 +319,13 @@ Examples:
278
319
  newo logs --type call --skill CreateActor # Skill calls for CreateActor
279
320
  newo logs --flow CACreatorFlow --follow # Tail logs for specific flow
280
321
  newo logs --json --per 100 # Get 100 logs as JSON
322
+ newo logs --type call --name Gen --json # Only Gen action calls
323
+
324
+ Notes:
325
+ The model used for a turn is in data.source.model of the --json output —
326
+ do NOT infer it from actor/agent names.
327
+ external_event_id (newo sandbox --json) correlates a chat turn with its
328
+ logs: newo logs --event-id <id>
281
329
  `);
282
330
  }
283
331
  //# sourceMappingURL=logs.js.map
@@ -1,14 +1,20 @@
1
1
  /**
2
2
  * Sandbox Chat Command Handler
3
3
  * Supports both single-command and interactive modes
4
+ *
5
+ * Usage:
6
+ * npx newo sandbox "Hello" --customer <idn> # Single message mode
7
+ * npx newo sandbox --actor <actor_id> "Follow up" # Continue existing chat
8
+ * npx newo sandbox "ping" --connector vibe_agent # Select specific connector (v3.7.5)
9
+ * npx newo sandbox --list-connectors # Show running sandbox connectors (v3.7.5)
10
+ * npx newo sandbox --file ./msg.txt --actor <id> # Message from file (v3.7.5)
11
+ * cat msg.txt | npx newo sandbox --stdin # Message from stdin (v3.7.5)
12
+ * npx newo sandbox "ping" --timeout 420 # Custom response timeout in seconds (v3.7.5)
13
+ * npx newo sandbox "ping" --json # Machine-readable output (v3.7.5)
4
14
  */
5
15
  import type { MultiCustomerConfig, CliArgs } from '../../types.js';
6
16
  /**
7
17
  * Handle sandbox command
8
- * Usage:
9
- * npx newo sandbox "Hello" --customer <idn> # Single message mode
10
- * npx newo sandbox --actor <actor_id> "Follow up" # Continue existing chat
11
- * npx newo sandbox --interactive # Interactive mode (TBD)
12
18
  */
13
19
  export declare function handleSandboxCommand(customerConfig: MultiCustomerConfig, args: CliArgs, verbose: boolean): Promise<void>;
14
20
  //# sourceMappingURL=sandbox.d.ts.map