@cspeach/cli 1.1.47 → 1.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/level.js +1 -1
- package/dist/agent/loop.js +7 -6
- package/dist/agent/sap-connection-adapter.js +2 -0
- package/dist/agent/system-prompt.js +2 -2
- package/dist/agent/tool-dispatch.js +13 -1
- package/dist/approvals/op-labels.js +49 -10
- package/dist/approvals/render.js +14 -23
- package/dist/approvals/risk-floor.js +196 -19
- package/dist/approvals/visible-changes.js +35 -0
- package/dist/commands/config-set.js +5 -5
- package/dist/commands/cost.js +2 -1
- package/dist/commands/help.js +1 -1
- package/dist/commands/project.js +301 -0
- package/dist/commands/team-plan.js +16 -6
- package/dist/commands/team.js +295 -68
- package/dist/config/loader.js +13 -0
- package/dist/cost/cost-log.js +7 -4
- package/dist/cost/credits-wire.js +11 -6
- package/dist/cost/credits.js +15 -0
- package/dist/cost/display-mode.js +16 -4
- package/dist/doctor/checks/zcspeach.js +96 -0
- package/dist/journey/chain.js +489 -61
- package/dist/journey/clean-core.js +61 -36
- package/dist/journey/flow.js +75 -2
- package/dist/journey/people.js +37 -0
- package/dist/journey/rebuild.js +7 -6
- package/dist/journey/run.js +129 -42
- package/dist/journey/trigger.js +16 -10
- package/dist/one-shot.js +1 -1
- package/dist/project-context/render.js +7 -0
- package/dist/projects/clean-core-confirm.js +166 -44
- package/dist/projects/email-template.js +56 -0
- package/dist/projects/extract-cca.js +8 -7
- package/dist/projects/index.js +0 -1
- package/dist/projects/merge-cca.js +0 -19
- package/dist/projects/merge-upgrade.js +2 -2
- package/dist/projects/project-folder.js +2 -1
- package/dist/projects/save-command.js +51 -14
- package/dist/register/decision-rules.js +2 -1
- package/dist/register/export-files.js +18 -0
- package/dist/register/picture.js +3 -3
- package/dist/register/project-transport.js +125 -0
- package/dist/register/records.js +25 -13
- package/dist/register/rules-edit.js +5 -0
- package/dist/register/status-file/progress-chart.js +17 -6
- package/dist/register/status-file/register-map.js +8 -5
- package/dist/register/status-file/register-model.js +55 -36
- package/dist/register/status-file/register-parts.js +97 -82
- package/dist/register/status-file/register-status-file.js +16 -7
- package/dist/register/status-file/report-model.js +293 -205
- package/dist/register/status-file/team-css.js +1 -1
- package/dist/register/status-html.js +11 -4
- package/dist/register/store.js +2 -2
- package/dist/register/workbooks.js +24 -13
- package/dist/renderer/footer-line.js +10 -2
- package/dist/renderer/status-footer.js +2 -1
- package/dist/renderer/tool-labels.js +2 -2
- package/dist/repl/builtin-commands.js +1 -1
- package/dist/repl/continue-skill.js +5 -0
- package/dist/repl/level-command.js +6 -15
- package/dist/repl.js +21 -8
- package/dist/router/router-core.js +5 -3
- package/dist/session/audit-export.js +4 -1
- package/dist/skill-catalog.js +4 -7
- package/dist/tools/_team-work-path.js +53 -0
- package/dist/tools/approval.js +97 -19
- package/dist/tools/filesystem/file-edit.js +8 -1
- package/dist/tools/filesystem/file-write.js +8 -1
- package/dist/tools/local-files.js +31 -0
- package/dist/tools/project/_merge-shared.js +2 -2
- package/dist/tools/project/_team-tool-shared.js +5 -0
- package/dist/tools/project/cca_merge.js +1 -11
- package/dist/tools/project/rules_propose.js +2 -1
- package/dist/tools/project/team_findings.js +81 -28
- package/dist/tools/project/team_lane_move.js +127 -0
- package/dist/tools/project/upgrade_merge_progress.js +8 -4
- package/dist/tools/subagent/schedule_create.js +131 -0
- package/dist/tools/text-elements/tools.js +7 -1
- package/dist/tools/tool-loading.js +1 -1
- package/dist/tools/update-method-intercept.js +18 -1
- package/dist/tools/write-pipeline.js +19 -5
- package/dist/ui/approval-modal.js +6 -3
- package/dist/ui/error-emitter.js +21 -0
- package/dist/ui/footer-line.js +19 -1
- package/dist/ui/header.js +41 -0
- package/dist/ui/ink-classifier-route.js +19 -0
- package/dist/ui/rich-error-box.js +9 -0
- package/dist/ui/sidebar.js +12 -0
- package/dist/ui/status-emitter.js +19 -0
- package/dist/ui/status-line.js +62 -0
- package/dist/ui/status-row.js +38 -0
- package/dist/ui/todo-panel.js +85 -0
- package/dist/ui/widgets/ask-form.js +250 -0
- package/dist/ui/widgets/ask-question-modal.js +178 -0
- package/fiori/zlas-inspection-lr/package.json +26 -0
- package/fiori/zlas-inspection-lr/ui5-local.yaml +45 -0
- package/fiori/zlas-inspection-lr/ui5.yaml +41 -0
- package/fiori/zlas-inspection-lr/webapp/Component.js +12 -0
- package/fiori/zlas-inspection-lr/webapp/i18n/i18n.properties +9 -0
- package/fiori/zlas-inspection-lr/webapp/index.html +35 -0
- package/fiori/zlas-inspection-lr/webapp/manifest.json +145 -0
- package/live-check-2026-10-17-1343.txt +41 -0
- package/package.json +121 -121
- package/test-harness/fe-draft/app.ts +0 -188
- package/test-harness/fe-draft/fe-draft.e2e.test.ts +0 -461
- package/test-harness/fe-draft/manifest-enhancer.unit.test.ts +0 -51
- package/test-harness/fe-draft/routes.ts +0 -224
- package/test-harness/fe-draft/routes.unit.test.ts +0 -45
- package/test-harness/fe-draft/server.ts +0 -225
- package/test-harness/fe-draft/variants.ts +0 -196
package/dist/config/loader.js
CHANGED
|
@@ -6,6 +6,8 @@ import { DEFAULT_SESSION_MODEL } from './model-defaults.js';
|
|
|
6
6
|
import { sanitiseStandaloneProfile } from '../sap/standalone-profile.js';
|
|
7
7
|
import { DEFAULT_STYLE, parseStyle } from '../agent/style.js';
|
|
8
8
|
import { DEFAULT_LEVEL, parseLevel } from '../agent/level.js';
|
|
9
|
+
import { setProvisionalCostDisplayMode } from '../cost/credits.js';
|
|
10
|
+
import { provisionalDisplayMode } from '../cost/display-mode.js';
|
|
9
11
|
/**
|
|
10
12
|
* Whitelist guard for WriteMode — rejects typos / stale values from disk so
|
|
11
13
|
* downstream code that trusts the WriteMode type doesn't see garbage.
|
|
@@ -148,7 +150,18 @@ export function resolveSapTls(sys) {
|
|
|
148
150
|
}
|
|
149
151
|
/** Tracks whether the one-time legacy-sslVerify deprecation note has printed. */
|
|
150
152
|
let legacySslVerifyNoticeShown = false;
|
|
153
|
+
/**
|
|
154
|
+
* Load the config, then set the cost display mode it implies until the server
|
|
155
|
+
* answers (2026-10-19). Every command loads the config first, so no path — one
|
|
156
|
+
* that never reaches initCredits included — can show dollars on a managed
|
|
157
|
+
* account by default.
|
|
158
|
+
*/
|
|
151
159
|
export async function loadConfig() {
|
|
160
|
+
const cfg = await readConfig();
|
|
161
|
+
setProvisionalCostDisplayMode(provisionalDisplayMode(cfg));
|
|
162
|
+
return cfg;
|
|
163
|
+
}
|
|
164
|
+
async function readConfig() {
|
|
152
165
|
try {
|
|
153
166
|
const raw = await fs.readFile(configFile(), 'utf-8');
|
|
154
167
|
// Strip a leading UTF-8 BOM (U+FEFF). Node's 'utf-8' read does NOT remove it,
|
package/dist/cost/cost-log.js
CHANGED
|
@@ -15,7 +15,7 @@ import fs from 'node:fs/promises';
|
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { sessionsDir } from '../config/paths.js';
|
|
17
17
|
import { computeCost } from './pricing.js';
|
|
18
|
-
import {
|
|
18
|
+
import { activeCostDisplayMode } from './credits.js';
|
|
19
19
|
/**
|
|
20
20
|
* Task 7 — summarise `message_start.message.input_transformations` (probe P4
|
|
21
21
|
* shape: `[{ type: 'thinking_dropped', path, reason }]`). Counts the
|
|
@@ -56,8 +56,10 @@ export function buildEntry(opts) {
|
|
|
56
56
|
turn: opts.turn,
|
|
57
57
|
model: opts.model,
|
|
58
58
|
tokens: opts.tokens,
|
|
59
|
-
//
|
|
60
|
-
|
|
59
|
+
// Only where dollars are shown (own key, or a permitted operator). Credits:
|
|
60
|
+
// this file sits on the customer's disk, so it never holds our cost
|
|
61
|
+
// (2026-10-19). Azure: no price for the customer's deployment.
|
|
62
|
+
cost: activeCostDisplayMode() === 'usd' ? computeCost(opts.model, opts.tokens) : 0,
|
|
61
63
|
duration_ms: opts.duration_ms,
|
|
62
64
|
};
|
|
63
65
|
if (opts.credits) {
|
|
@@ -77,7 +79,8 @@ export function buildEntry(opts) {
|
|
|
77
79
|
if (opts.input_transformations !== undefined)
|
|
78
80
|
entry.input_transformations = opts.input_transformations;
|
|
79
81
|
if (opts.reader && opts.reader.calls > 0) {
|
|
80
|
-
|
|
82
|
+
if (activeCostDisplayMode() === 'usd')
|
|
83
|
+
entry.reader_cost_usd = Math.round(opts.reader.cost * 10000) / 10000;
|
|
81
84
|
entry.reader_calls = opts.reader.calls;
|
|
82
85
|
}
|
|
83
86
|
return entry;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* a one-liner and no module has to re-derive the display mode for itself.
|
|
23
23
|
*/
|
|
24
24
|
import { fetchBalance, globalCredits, setCostDisplayMode, creditsActive, } from './credits.js';
|
|
25
|
-
import { costDisplayMode } from './display-mode.js';
|
|
25
|
+
import { costDisplayMode, provisionalDisplayMode } from './display-mode.js';
|
|
26
26
|
import { globalStore } from '../ui/sap-state-store.js';
|
|
27
27
|
let wiredCfg = null;
|
|
28
28
|
let wiredBearer = null;
|
|
@@ -39,14 +39,14 @@ function publish() {
|
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Startup: resolve the display mode and seed the opening balance.
|
|
42
|
-
* Never throws
|
|
43
|
-
*
|
|
42
|
+
* Never throws. On any failure a managed account stays in credits (2026-10-19:
|
|
43
|
+
* this used to fall back to dollars — our cost on the customer's screen).
|
|
44
44
|
*/
|
|
45
45
|
export async function initCredits(cfg, getBearer) {
|
|
46
46
|
try {
|
|
47
47
|
// Non-managed modes can't be metered by us — skip the round-trip entirely.
|
|
48
48
|
if (cfg.llm?.mode !== 'managed') {
|
|
49
|
-
setCostDisplayMode(cfg
|
|
49
|
+
setCostDisplayMode(provisionalDisplayMode(cfg));
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
const info = await fetchBalance(cfg, getBearer);
|
|
@@ -60,8 +60,13 @@ export async function initCredits(cfg, getBearer) {
|
|
|
60
60
|
publish();
|
|
61
61
|
}
|
|
62
62
|
catch {
|
|
63
|
-
// Startup must never be blocked by the meter.
|
|
64
|
-
setCostDisplayMode(
|
|
63
|
+
// Startup must never be blocked by the meter — and never shows our cost.
|
|
64
|
+
setCostDisplayMode(provisionalDisplayMode(cfg));
|
|
65
|
+
// The footer reads the store's flag, so publish the mode here too.
|
|
66
|
+
if (creditsActive()) {
|
|
67
|
+
globalCredits.start(null);
|
|
68
|
+
publish();
|
|
69
|
+
}
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
/**
|
package/dist/cost/credits.js
CHANGED
|
@@ -61,6 +61,8 @@ export async function fetchBalance(cfg, getBearer) {
|
|
|
61
61
|
};
|
|
62
62
|
if (b.unprovisioned === true)
|
|
63
63
|
info.unprovisioned = true;
|
|
64
|
+
if (b.cost_view === true)
|
|
65
|
+
info.cost_view = true;
|
|
64
66
|
return info;
|
|
65
67
|
}
|
|
66
68
|
catch {
|
|
@@ -198,9 +200,21 @@ export function formatCreditsLine(s) {
|
|
|
198
200
|
*/
|
|
199
201
|
export const globalCredits = new CreditsTracker();
|
|
200
202
|
let activeMode = 'usd';
|
|
203
|
+
/** True once startup (initCredits) has resolved the mode with the server's answer. */
|
|
204
|
+
let resolved = false;
|
|
201
205
|
/** Publish the resolved display mode for the whole process. */
|
|
202
206
|
export function setCostDisplayMode(mode) {
|
|
203
207
|
activeMode = mode;
|
|
208
|
+
resolved = true;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The mode implied by the config alone, set when the config loads. Applies
|
|
212
|
+
* only until startup resolves the real mode, so a later config reload never
|
|
213
|
+
* overrides the server's answer (2026-10-19).
|
|
214
|
+
*/
|
|
215
|
+
export function setProvisionalCostDisplayMode(mode) {
|
|
216
|
+
if (!resolved)
|
|
217
|
+
activeMode = mode;
|
|
204
218
|
}
|
|
205
219
|
/** The resolved display mode. Defaults to 'usd' until startup resolves it —
|
|
206
220
|
* fail-safe for BYOK/local/one-shot paths that never call the setter. */
|
|
@@ -218,4 +232,5 @@ export function tokensOnlyActive() {
|
|
|
218
232
|
/** Test seam — reset the process-wide mode between cases. */
|
|
219
233
|
export function resetCostDisplayMode() {
|
|
220
234
|
activeMode = 'usd';
|
|
235
|
+
resolved = false;
|
|
221
236
|
}
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
* - managed but unmetered (or the balance fetch failed): we have no
|
|
16
16
|
* credit truth to show. Fall back to the existing dollar rendering so
|
|
17
17
|
* the meter is never blank.
|
|
18
|
-
* - `show_cogs = on
|
|
19
|
-
* on a managed account (margin analysis,
|
|
18
|
+
* - `show_cogs = on` AND the server's `cost_view` for this account: our own
|
|
19
|
+
* operator view of real COGS on a managed account (margin analysis,
|
|
20
|
+
* pricing work). The laptop setting alone does nothing (owner 2026-10-19:
|
|
21
|
+
* any customer could type it); the server must say yes as well.
|
|
20
22
|
*
|
|
21
23
|
* Callers must treat this as the single source of truth — no site may
|
|
22
24
|
* re-derive the mode from `cfg.llm.mode` alone.
|
|
@@ -28,13 +30,23 @@ export function costDisplayMode(cfg, balanceInfo) {
|
|
|
28
30
|
return 'tokens';
|
|
29
31
|
if (cfg.llm?.mode !== 'managed')
|
|
30
32
|
return 'usd';
|
|
31
|
-
if (cfg.show_cogs === true)
|
|
33
|
+
if (cfg.show_cogs === true && balanceInfo?.cost_view === true)
|
|
32
34
|
return 'usd';
|
|
33
35
|
// 2026-09-17 — a managed account NEVER falls back to dollars. Before, a
|
|
34
36
|
// balance lookup that failed or took over 2.5s at start (slow network on a
|
|
35
37
|
// customer laptop) dropped the display to USD — our cost on their screen,
|
|
36
38
|
// exactly what credits mode exists to prevent. Unknown or unmetered
|
|
37
39
|
// balances render "credits: updating…" until the next good sample.
|
|
38
|
-
void balanceInfo;
|
|
39
40
|
return 'credits';
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* The mode before the server has answered — set the moment the config loads,
|
|
44
|
+
* so nothing printed early can show dollars on a managed account.
|
|
45
|
+
*/
|
|
46
|
+
export function provisionalDisplayMode(cfg) {
|
|
47
|
+
if (cfg.llm?.mode === 'azure')
|
|
48
|
+
return 'tokens';
|
|
49
|
+
if (cfg.llm?.mode === 'managed')
|
|
50
|
+
return 'credits';
|
|
51
|
+
return 'usd';
|
|
52
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import keychain from '../../auth/keychain.js';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { loadConfig, resolveSapTls } from '../../config/loader.js';
|
|
4
|
+
import { probeGet } from './_http-probe.js';
|
|
5
|
+
import { sapChecksSkipped, standaloneSkip } from './_standalone-skip.js';
|
|
6
|
+
const SAP_SERVICE = 'cspeach-sap';
|
|
7
|
+
export async function checkZcspeach() {
|
|
8
|
+
const cfg = await loadConfig();
|
|
9
|
+
// Standalone mode ONLY — an opted-in paste-mode user has nothing to probe.
|
|
10
|
+
// Everything below (including the no-alias failure) is unchanged.
|
|
11
|
+
if (sapChecksSkipped(cfg))
|
|
12
|
+
return standaloneSkip('zcspeach');
|
|
13
|
+
const systems = Object.entries(cfg.sap);
|
|
14
|
+
if (systems.length === 0) {
|
|
15
|
+
return {
|
|
16
|
+
name: 'zcspeach',
|
|
17
|
+
ok: false,
|
|
18
|
+
message: 'no SAP systems configured',
|
|
19
|
+
hint: 'Run: cspeach config add-sap',
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const results = [];
|
|
23
|
+
let anyFailed = false;
|
|
24
|
+
for (const [alias, sys] of systems) {
|
|
25
|
+
const protocol = sys.useSsl ? 'https' : 'http';
|
|
26
|
+
const url = `${protocol}://${sys.host}:${sys.port}/sap/bc/zcspeach/discovery`;
|
|
27
|
+
let pwd = null;
|
|
28
|
+
try {
|
|
29
|
+
pwd = await keychain.getPassword(SAP_SERVICE, alias);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// keychain unavailable — fall through, will be skipped below
|
|
33
|
+
}
|
|
34
|
+
if (!pwd) {
|
|
35
|
+
results.push(`${alias}: skipped (no password in keychain)`);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
// TLS posture (2026-06-14): verification ON by default; resolveSapTls maps
|
|
39
|
+
// the per-alias config (ca_cert_path / insecure_skip_tls_verify, legacy
|
|
40
|
+
// sslVerify:false fallback). See checks/_http-probe.ts.
|
|
41
|
+
const tls = resolveSapTls(sys);
|
|
42
|
+
let ca;
|
|
43
|
+
if (tls.mode === 'ca-trusted' && tls.caCertPath) {
|
|
44
|
+
try {
|
|
45
|
+
ca = await readFile(tls.caCertPath);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
results.push(`${alias}: CA unreadable at ${tls.caCertPath} (${e.message})`);
|
|
49
|
+
anyFailed = true;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const r = await probeGet(url, {
|
|
54
|
+
rejectUnauthorized: tls.mode !== 'insecure',
|
|
55
|
+
ca,
|
|
56
|
+
auth: `${sys.username}:${pwd}`,
|
|
57
|
+
accept: 'application/json',
|
|
58
|
+
timeoutMs: 5000,
|
|
59
|
+
});
|
|
60
|
+
if (r.error) {
|
|
61
|
+
results.push(`${alias}: ${r.error}`);
|
|
62
|
+
anyFailed = true;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (r.status < 200 || r.status >= 300) {
|
|
66
|
+
results.push(`${alias}: HTTP ${r.status} from /zcspeach/discovery`);
|
|
67
|
+
anyFailed = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
let body;
|
|
71
|
+
try {
|
|
72
|
+
body = JSON.parse(r.body);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
results.push(`${alias}: invalid JSON from /zcspeach/discovery`);
|
|
76
|
+
anyFailed = true;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (body.status !== 'ok') {
|
|
80
|
+
results.push(`${alias}: discovery returned status=${body.status ?? '<missing>'}`);
|
|
81
|
+
anyFailed = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const ver = body.data?.version ?? '<missing>';
|
|
85
|
+
const ops = body.data?.operations?.length ?? 0;
|
|
86
|
+
results.push(`${alias}: v${ver}, ${ops} ops`);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
name: 'zcspeach',
|
|
90
|
+
ok: !anyFailed,
|
|
91
|
+
message: results.join('; '),
|
|
92
|
+
hint: anyFailed
|
|
93
|
+
? 'SICF node /sap/bc/zcspeach must be active; ensure SAP_BASIS supports the framework'
|
|
94
|
+
: undefined,
|
|
95
|
+
};
|
|
96
|
+
}
|