@ishlabs/cli 0.8.5 → 0.10.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.
- package/README.md +55 -6
- package/dist/auth.d.ts +23 -4
- package/dist/auth.js +165 -39
- package/dist/commands/ask.d.ts +12 -0
- package/dist/commands/ask.js +127 -2
- package/dist/commands/chat.d.ts +17 -0
- package/dist/commands/chat.js +589 -0
- package/dist/commands/iteration.js +232 -13
- package/dist/commands/secret.d.ts +20 -0
- package/dist/commands/secret.js +246 -0
- package/dist/commands/source.js +24 -2
- package/dist/commands/study-run.d.ts +38 -0
- package/dist/commands/study-run.js +199 -80
- package/dist/commands/study-tester.js +17 -2
- package/dist/commands/study.js +311 -39
- package/dist/commands/workspace.js +81 -0
- package/dist/config.d.ts +7 -0
- package/dist/connect.d.ts +3 -0
- package/dist/connect.js +359 -24
- package/dist/index.js +67 -9
- package/dist/lib/alias-hydrate.d.ts +42 -0
- package/dist/lib/alias-hydrate.js +175 -0
- package/dist/lib/alias-store.d.ts +1 -0
- package/dist/lib/alias-store.js +28 -1
- package/dist/lib/auth.js +11 -3
- package/dist/lib/chat-endpoint-formatters.d.ts +39 -0
- package/dist/lib/chat-endpoint-formatters.js +104 -0
- package/dist/lib/command-helpers.d.ts +18 -0
- package/dist/lib/command-helpers.js +188 -53
- package/dist/lib/docs.js +662 -34
- package/dist/lib/modality.d.ts +42 -0
- package/dist/lib/modality.js +192 -0
- package/dist/lib/output.d.ts +41 -0
- package/dist/lib/output.js +453 -19
- package/dist/lib/paths.d.ts +1 -0
- package/dist/lib/paths.js +3 -0
- package/dist/lib/skill-content.js +183 -13
- package/dist/lib/types.d.ts +15 -0
- package/package.json +3 -3
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort alias-cache hydration on alias-miss (Pattern F).
|
|
3
|
+
*
|
|
4
|
+
* The CLI persists aliases to ``~/.ish/aliases.json``, so the cache survives
|
|
5
|
+
* across processes. Where it bites: the file is missing/empty (fresh install,
|
|
6
|
+
* `rm ~/.ish/aliases.json`, agent running in a sandbox with a fresh
|
|
7
|
+
* `ISH_HOME`) and the agent has an alias from a prior process or the docs.
|
|
8
|
+
*
|
|
9
|
+
* ``resolveIdAsync(input, client, hints?)`` mirrors the sync ``resolveId``
|
|
10
|
+
* contract but, on alias-miss, attempts a single ``GET /list`` to repopulate
|
|
11
|
+
* the cache before retrying. The hydrate is BEST-EFFORT: a failing list
|
|
12
|
+
* call is swallowed and the canonical "Unknown alias" error fires with the
|
|
13
|
+
* actionable suggestion in the message.
|
|
14
|
+
*
|
|
15
|
+
* For prefixes whose list endpoint requires a parent ID (study/iteration/
|
|
16
|
+
* ask/etc), the caller passes ``hints`` carrying the parent (workspaceId,
|
|
17
|
+
* studyId). Without a parent we skip the hydrate — global N+1 fan-out is
|
|
18
|
+
* too expensive for a papercut.
|
|
19
|
+
*/
|
|
20
|
+
import { ALIAS_PREFIX, resolveId, tagAlias } from "./alias-store.js";
|
|
21
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22
|
+
const ALIAS_RE = /^[a-z]+-[0-9a-f]{3,}$|^[a-z]+\d{2,}$/;
|
|
23
|
+
function aliasPrefix(value) {
|
|
24
|
+
if (!ALIAS_RE.test(value))
|
|
25
|
+
return null;
|
|
26
|
+
const m = value.match(/^([a-z]+)/);
|
|
27
|
+
return m ? m[1] : null;
|
|
28
|
+
}
|
|
29
|
+
function isAliasShape(value) {
|
|
30
|
+
return ALIAS_RE.test(value);
|
|
31
|
+
}
|
|
32
|
+
function isUuid(value) {
|
|
33
|
+
return UUID_RE.test(value);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Try to resolve a parent ID — accepts either a UUID or a known alias. Used
|
|
37
|
+
* to extract ``workspaceId`` / ``studyId`` from hints before we fan out to a
|
|
38
|
+
* scoped list endpoint. Returns ``null`` if the value can't be resolved
|
|
39
|
+
* cheaply (e.g. it's an alias that's also missing from the cache).
|
|
40
|
+
*/
|
|
41
|
+
function resolveParent(value) {
|
|
42
|
+
if (!value)
|
|
43
|
+
return null;
|
|
44
|
+
if (isUuid(value))
|
|
45
|
+
return value;
|
|
46
|
+
// Try the sync resolver but never throw.
|
|
47
|
+
try {
|
|
48
|
+
return resolveId(value);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Hit a list endpoint and tag every returned ID under the given prefix.
|
|
56
|
+
* Silently swallows network/auth failures — we only ever block the
|
|
57
|
+
* underlying call when the alias is genuinely unknown after hydrate.
|
|
58
|
+
*/
|
|
59
|
+
async function hydrateList(client, prefix, path, params) {
|
|
60
|
+
let data;
|
|
61
|
+
try {
|
|
62
|
+
data = await client.get(path, params);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let items = [];
|
|
68
|
+
if (Array.isArray(data)) {
|
|
69
|
+
items = data;
|
|
70
|
+
}
|
|
71
|
+
else if (typeof data === "object" && data !== null && "items" in data) {
|
|
72
|
+
const inner = data.items;
|
|
73
|
+
if (Array.isArray(inner))
|
|
74
|
+
items = inner;
|
|
75
|
+
}
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
if (typeof item !== "object" || item === null)
|
|
78
|
+
continue;
|
|
79
|
+
const id = item.id;
|
|
80
|
+
if (typeof id === "string" && id.length > 0) {
|
|
81
|
+
try {
|
|
82
|
+
tagAlias(prefix, id);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Ignore — bad UUIDs are not catastrophic.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Best-effort hydrate of the alias cache for ``alias``'s entity type. Returns
|
|
92
|
+
* silently on success or any failure — the caller checks the cache after
|
|
93
|
+
* this returns.
|
|
94
|
+
*/
|
|
95
|
+
export async function hydrateForAlias(client, alias, hints = {}) {
|
|
96
|
+
const prefix = aliasPrefix(alias);
|
|
97
|
+
if (!prefix)
|
|
98
|
+
return;
|
|
99
|
+
const ws = resolveParent(hints.workspaceId);
|
|
100
|
+
const study = resolveParent(hints.studyId);
|
|
101
|
+
switch (prefix) {
|
|
102
|
+
case ALIAS_PREFIX.workspace: {
|
|
103
|
+
// Top-level — no parent.
|
|
104
|
+
await hydrateList(client, ALIAS_PREFIX.workspace, "/products");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
case ALIAS_PREFIX.study: {
|
|
108
|
+
if (!ws)
|
|
109
|
+
return;
|
|
110
|
+
await hydrateList(client, ALIAS_PREFIX.study, `/products/${ws}/studies`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
case ALIAS_PREFIX.iteration: {
|
|
114
|
+
if (!study)
|
|
115
|
+
return;
|
|
116
|
+
await hydrateList(client, ALIAS_PREFIX.iteration, `/studies/${study}/iterations`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
case ALIAS_PREFIX.testerProfile: {
|
|
120
|
+
if (!ws)
|
|
121
|
+
return;
|
|
122
|
+
await hydrateList(client, ALIAS_PREFIX.testerProfile, "/tester-profiles", { workspace_id: ws, type: "all", limit: "200" });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
case ALIAS_PREFIX.ask: {
|
|
126
|
+
if (!ws)
|
|
127
|
+
return;
|
|
128
|
+
await hydrateList(client, ALIAS_PREFIX.ask, `/products/${ws}/asks`);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
case ALIAS_PREFIX.chatEndpoint: {
|
|
132
|
+
if (!ws)
|
|
133
|
+
return;
|
|
134
|
+
await hydrateList(client, ALIAS_PREFIX.chatEndpoint, `/products/${ws}/chatbot-endpoints`);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
// No cheap single-call hydrate for the rest:
|
|
138
|
+
// tester (`t-`) — scoped to iteration
|
|
139
|
+
// ask round (`r-`) — nested on the ask
|
|
140
|
+
// audience source (`tps-`) — fetched per-id
|
|
141
|
+
// simulation config (`c-`) — no list endpoint yet
|
|
142
|
+
default:
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Async sibling of ``resolveId``: same UUID-or-alias contract, but on
|
|
148
|
+
* alias-miss attempts a best-effort hydrate via the matching list endpoint
|
|
149
|
+
* before retrying. Falls back to the canonical "Unknown alias" error
|
|
150
|
+
* (with named list-command suggestion) when the alias is still missing
|
|
151
|
+
* after the hydrate.
|
|
152
|
+
*
|
|
153
|
+
* Use this at command-handler entry points where the agent supplies an
|
|
154
|
+
* alias and the same call carries enough context (workspace / study) to
|
|
155
|
+
* scope the hydrate cheaply.
|
|
156
|
+
*/
|
|
157
|
+
export async function resolveIdAsync(input, client, hints = {}) {
|
|
158
|
+
if (isUuid(input))
|
|
159
|
+
return input;
|
|
160
|
+
if (!isAliasShape(input)) {
|
|
161
|
+
// Fall through to the sync resolver so the "Invalid ID" guidance fires.
|
|
162
|
+
return resolveId(input);
|
|
163
|
+
}
|
|
164
|
+
// Cheap-path: alias already in store.
|
|
165
|
+
try {
|
|
166
|
+
return resolveId(input);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
// Cache miss — attempt best-effort hydrate.
|
|
170
|
+
await hydrateForAlias(client, input, hints);
|
|
171
|
+
// Retry — sync resolver re-reads ``aliases.json`` from disk on every
|
|
172
|
+
// call (see loadAliases), so the freshly tagged entries are visible.
|
|
173
|
+
return resolveId(input);
|
|
174
|
+
}
|
|
175
|
+
}
|
package/dist/lib/alias-store.js
CHANGED
|
@@ -20,6 +20,7 @@ export const ALIAS_PREFIX = {
|
|
|
20
20
|
job: "j",
|
|
21
21
|
ask: "a",
|
|
22
22
|
askRound: "r",
|
|
23
|
+
chatEndpoint: "ep",
|
|
23
24
|
};
|
|
24
25
|
/** Format a number with zero-padding (minimum 2 digits). */
|
|
25
26
|
function padNum(n) {
|
|
@@ -110,6 +111,32 @@ export function deterministicAlias(prefix, uuid) {
|
|
|
110
111
|
}
|
|
111
112
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
112
113
|
const ALIAS_RE = /^[a-z]+-[0-9a-f]{3,}$|^[a-z]+\d{2,}$/;
|
|
114
|
+
/**
|
|
115
|
+
* Suggested `ish ... list` command per alias prefix. Surfaced in the
|
|
116
|
+
* unknown-alias error (Pattern F) so the agent doesn't have to guess
|
|
117
|
+
* which list command to run.
|
|
118
|
+
*/
|
|
119
|
+
const HYDRATE_HINT = {
|
|
120
|
+
w: "ish workspace list",
|
|
121
|
+
s: "ish study list",
|
|
122
|
+
i: "ish iteration list --study <study-id>",
|
|
123
|
+
tp: "ish profile list",
|
|
124
|
+
tps: "ish source list",
|
|
125
|
+
t: "ish tester get <tester-id>",
|
|
126
|
+
c: "ish config list",
|
|
127
|
+
a: "ish ask list",
|
|
128
|
+
r: "ish ask get <ask-id>",
|
|
129
|
+
ep: "ish chat endpoint list",
|
|
130
|
+
// Legacy two-letter prefixes the deterministic generator may have
|
|
131
|
+
// produced before; defaults below cover anything else.
|
|
132
|
+
};
|
|
133
|
+
function hintForPrefix(alias) {
|
|
134
|
+
// Pull the leading alpha run, which is the prefix.
|
|
135
|
+
const m = alias.match(/^([a-z]+)/);
|
|
136
|
+
if (!m)
|
|
137
|
+
return "the matching list command";
|
|
138
|
+
return HYDRATE_HINT[m[1]] ?? "the matching list command";
|
|
139
|
+
}
|
|
113
140
|
/**
|
|
114
141
|
* Resolve a short alias to a full UUID, or validate and pass through a full UUID.
|
|
115
142
|
*
|
|
@@ -130,7 +157,7 @@ export function resolveId(input) {
|
|
|
130
157
|
const uuid = aliases[input];
|
|
131
158
|
if (uuid)
|
|
132
159
|
return uuid;
|
|
133
|
-
throw new Error(`Unknown alias "${input}". Run
|
|
160
|
+
throw new Error(`Unknown alias "${input}". Run \`${hintForPrefix(input)}\` first to generate aliases.`);
|
|
134
161
|
}
|
|
135
162
|
// 3. Anything else — fail with helpful guidance
|
|
136
163
|
throw new Error(`Invalid ID "${input}". Use a short alias (e.g. w-a3f, s-b2c) or a full UUID.\n` +
|
package/dist/lib/auth.js
CHANGED
|
@@ -24,8 +24,10 @@ async function verifyToken(token, apiUrl) {
|
|
|
24
24
|
return resp.status !== 401 && resp.status !== 403;
|
|
25
25
|
}
|
|
26
26
|
catch {
|
|
27
|
-
// Network
|
|
28
|
-
|
|
27
|
+
// Network blip on the best-effort probe. The subsequent API call will
|
|
28
|
+
// surface the real auth failure (with a proper exit code 3) if there
|
|
29
|
+
// is one, so don't pollute stderr on every command — it fired on
|
|
30
|
+
// every successful run during Phase A (Pattern F / C4-finding-5).
|
|
29
31
|
return true;
|
|
30
32
|
}
|
|
31
33
|
}
|
|
@@ -57,8 +59,14 @@ export async function resolveToken(tokenArg, apiUrl, tokenFileArg) {
|
|
|
57
59
|
let accessToken = config.access_token;
|
|
58
60
|
// Refresh if expired or close to expiry
|
|
59
61
|
if (isTokenExpired(accessToken)) {
|
|
62
|
+
if (!config.oauth_client_id) {
|
|
63
|
+
throw new Error('Saved tokens are missing oauth_client_id. Run "ish login" to re-authenticate.');
|
|
64
|
+
}
|
|
60
65
|
try {
|
|
61
|
-
const tokens = await refreshTokens(config.refresh_token, {
|
|
66
|
+
const tokens = await refreshTokens(config.refresh_token, {
|
|
67
|
+
accessToken,
|
|
68
|
+
clientId: config.oauth_client_id,
|
|
69
|
+
});
|
|
62
70
|
accessToken = tokens.accessToken;
|
|
63
71
|
config.access_token = tokens.accessToken;
|
|
64
72
|
config.refresh_token = tokens.refreshToken;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean-vs-verbose projection for ChatbotEndpointResponse.
|
|
3
|
+
*
|
|
4
|
+
* The backend returns a nested camelCase shape (id, name, productId, config,
|
|
5
|
+
* isTunnelBacked, createdAt, updatedAt). The lean projection keeps only the
|
|
6
|
+
* fields an agent typically branches on: id/alias/name, transport, the
|
|
7
|
+
* outgoing url + method, the incoming messagePath, the slot-path count, and
|
|
8
|
+
* isTunnelBacked. `--verbose` (or piped) passes the raw response.
|
|
9
|
+
*/
|
|
10
|
+
export interface OutgoingHttp {
|
|
11
|
+
url?: string;
|
|
12
|
+
method?: string;
|
|
13
|
+
mode?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface IncomingHttp {
|
|
17
|
+
messagePath?: string;
|
|
18
|
+
slotsContainerPaths?: string[];
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
}
|
|
21
|
+
export interface ChatbotEndpointConfig {
|
|
22
|
+
transport?: string;
|
|
23
|
+
outgoing?: OutgoingHttp;
|
|
24
|
+
incoming?: IncomingHttp;
|
|
25
|
+
isTunnelBacked?: boolean;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface ChatbotEndpointRow {
|
|
29
|
+
id?: string;
|
|
30
|
+
name?: string;
|
|
31
|
+
productId?: string;
|
|
32
|
+
config?: ChatbotEndpointConfig;
|
|
33
|
+
isTunnelBacked?: boolean;
|
|
34
|
+
[key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/** Return the round-trippable envelope used by `endpoint get --verbose`. */
|
|
37
|
+
export declare function envelopeFromRow(row: ChatbotEndpointRow): Record<string, unknown>;
|
|
38
|
+
export declare function formatChatEndpointList(rows: ChatbotEndpointRow[], json: boolean, verbose: boolean): void;
|
|
39
|
+
export declare function formatChatEndpointDetail(row: ChatbotEndpointRow, json: boolean, verbose: boolean): void;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean-vs-verbose projection for ChatbotEndpointResponse.
|
|
3
|
+
*
|
|
4
|
+
* The backend returns a nested camelCase shape (id, name, productId, config,
|
|
5
|
+
* isTunnelBacked, createdAt, updatedAt). The lean projection keeps only the
|
|
6
|
+
* fields an agent typically branches on: id/alias/name, transport, the
|
|
7
|
+
* outgoing url + method, the incoming messagePath, the slot-path count, and
|
|
8
|
+
* isTunnelBacked. `--verbose` (or piped) passes the raw response.
|
|
9
|
+
*/
|
|
10
|
+
import { tagAlias, ALIAS_PREFIX } from "./alias-store.js";
|
|
11
|
+
import { output, printTable } from "./output.js";
|
|
12
|
+
function leanRow(row) {
|
|
13
|
+
const cfg = row.config ?? {};
|
|
14
|
+
const out = {};
|
|
15
|
+
if (row.id)
|
|
16
|
+
out.alias = tagAlias(ALIAS_PREFIX.chatEndpoint, row.id);
|
|
17
|
+
if (row.id)
|
|
18
|
+
out.id = row.id;
|
|
19
|
+
if (row.name)
|
|
20
|
+
out.name = row.name;
|
|
21
|
+
if (cfg.transport)
|
|
22
|
+
out.transport = cfg.transport;
|
|
23
|
+
out.is_tunnel_backed = Boolean(row.isTunnelBacked);
|
|
24
|
+
if (cfg.outgoing?.url)
|
|
25
|
+
out.url = cfg.outgoing.url;
|
|
26
|
+
if (cfg.outgoing?.method)
|
|
27
|
+
out.method = cfg.outgoing.method;
|
|
28
|
+
if (cfg.outgoing?.mode)
|
|
29
|
+
out.mode = cfg.outgoing.mode;
|
|
30
|
+
if (cfg.incoming?.messagePath)
|
|
31
|
+
out.message_path = cfg.incoming.messagePath;
|
|
32
|
+
const slotsCount = Array.isArray(cfg.incoming?.slotsContainerPaths)
|
|
33
|
+
? cfg.incoming.slotsContainerPaths.length
|
|
34
|
+
: 0;
|
|
35
|
+
out.slots_paths = slotsCount;
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/** Return the round-trippable envelope used by `endpoint get --verbose`. */
|
|
39
|
+
export function envelopeFromRow(row) {
|
|
40
|
+
return {
|
|
41
|
+
id: row.id,
|
|
42
|
+
name: row.name,
|
|
43
|
+
isTunnelBacked: Boolean(row.isTunnelBacked),
|
|
44
|
+
config: row.config ?? {},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export function formatChatEndpointList(rows, json, verbose) {
|
|
48
|
+
if (json) {
|
|
49
|
+
if (verbose) {
|
|
50
|
+
output(rows, true);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
output(rows.map(leanRow), true);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (rows.length === 0) {
|
|
57
|
+
console.log("No chatbot endpoints.");
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const lean = rows.map(leanRow);
|
|
61
|
+
printTable(["#", "NAME", "TRANSPORT", "URL", "METHOD", "MODE", "TUNNEL"], lean.map((r) => [
|
|
62
|
+
String(r.alias ?? r.id ?? ""),
|
|
63
|
+
String(r.name ?? ""),
|
|
64
|
+
String(r.transport ?? "-"),
|
|
65
|
+
String(r.url ?? "-"),
|
|
66
|
+
String(r.method ?? "-"),
|
|
67
|
+
String(r.mode ?? "-"),
|
|
68
|
+
r.is_tunnel_backed ? "yes" : "no",
|
|
69
|
+
]));
|
|
70
|
+
}
|
|
71
|
+
export function formatChatEndpointDetail(row, json, verbose) {
|
|
72
|
+
if (json) {
|
|
73
|
+
if (verbose) {
|
|
74
|
+
output(envelopeFromRow(row), true);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
output(leanRow(row), true);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const cfg = row.config ?? {};
|
|
81
|
+
const alias = row.id ? tagAlias(ALIAS_PREFIX.chatEndpoint, row.id) : "-";
|
|
82
|
+
console.log(`${row.name || "Untitled"} (${alias})`);
|
|
83
|
+
const meta = [];
|
|
84
|
+
if (cfg.transport)
|
|
85
|
+
meta.push(String(cfg.transport));
|
|
86
|
+
if (row.isTunnelBacked)
|
|
87
|
+
meta.push("tunnel-backed");
|
|
88
|
+
if (meta.length > 0)
|
|
89
|
+
console.log(meta.join(" · "));
|
|
90
|
+
if (cfg.outgoing) {
|
|
91
|
+
console.log("");
|
|
92
|
+
console.log(` URL ${cfg.outgoing.url ?? "-"}`);
|
|
93
|
+
console.log(` Method ${cfg.outgoing.method ?? "-"}`);
|
|
94
|
+
console.log(` Mode ${cfg.outgoing.mode ?? "-"}`);
|
|
95
|
+
}
|
|
96
|
+
if (cfg.incoming) {
|
|
97
|
+
console.log("");
|
|
98
|
+
console.log(` Message path ${cfg.incoming.messagePath ?? "-"}`);
|
|
99
|
+
const slots = Array.isArray(cfg.incoming.slotsContainerPaths)
|
|
100
|
+
? cfg.incoming.slotsContainerPaths.length
|
|
101
|
+
: 0;
|
|
102
|
+
console.log(` Slot paths ${slots}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -123,6 +123,12 @@ export declare function confirmDestructive(prompt: string, opts: {
|
|
|
123
123
|
export declare function resolveWorkspace(explicit?: string): string;
|
|
124
124
|
export declare function resolveStudy(explicit?: string): string;
|
|
125
125
|
export declare function resolveAsk(explicit?: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Resolve a chat endpoint id from (in order): the positional argument, the
|
|
128
|
+
* `--endpoint <id>` flag, the `ISH_CHAT_ENDPOINT` env var, or the active
|
|
129
|
+
* endpoint persisted by `ish chat endpoint use`. Throws when none are set.
|
|
130
|
+
*/
|
|
131
|
+
export declare function resolveChatEndpoint(positional?: string, flag?: string): string;
|
|
126
132
|
/** Commander option-collector for repeatable flags (e.g. `--variant text:"..."` repeated). */
|
|
127
133
|
export declare function collectRepeatable(value: string, prev?: string[]): string[];
|
|
128
134
|
/**
|
|
@@ -150,4 +156,16 @@ export declare function parseWaitTimeout(raw: string | undefined, defaultMs?: nu
|
|
|
150
156
|
* body. Resolvers (`resolveWorkspace`, `resolveAudienceProfileIds`) ignore
|
|
151
157
|
* unused values.
|
|
152
158
|
*/
|
|
159
|
+
/**
|
|
160
|
+
* Read a `--*-config <file>` style flag value, treating "-" as "read from
|
|
161
|
+
* stdin" and any other value as a file path on disk. Trailing newlines on
|
|
162
|
+
* stdin input are stripped so the resulting string parses cleanly as JSON.
|
|
163
|
+
*
|
|
164
|
+
* Throws when "-" is passed but stdin is a TTY (no upstream pipe).
|
|
165
|
+
*
|
|
166
|
+
* Mirrors the readSecretFlag pattern in src/commands/workspace.ts; extracted
|
|
167
|
+
* so every `--<x>-config <file>` flag across commands shares one
|
|
168
|
+
* implementation.
|
|
169
|
+
*/
|
|
170
|
+
export declare function readFileOrStdin(path: string): Promise<string>;
|
|
153
171
|
export declare function injectGlobalWorkspaceOption(program: Command): void;
|