@skrr-ai/cli 0.1.40 → 0.1.41
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/base-command.d.ts +9 -0
- package/dist/base-command.js +18 -3
- package/dist/commands/agent-shares/list-for.js +4 -11
- package/dist/commands/agent-shares/received.js +4 -11
- package/dist/commands/agent-shares/sent.js +4 -11
- package/dist/commands/agent-shares/with.js +3 -10
- package/dist/commands/agents/activity.js +1 -1
- package/dist/commands/agents/api-actions/add.js +6 -0
- package/dist/commands/agents/chat.js +6 -1
- package/dist/commands/agents/create.js +6 -1
- package/dist/commands/agents/manager/ensure.d.ts +18 -0
- package/dist/commands/agents/manager/ensure.js +56 -0
- package/dist/commands/agents/show.js +4 -0
- package/dist/commands/agents/tools/auth.d.ts +6 -0
- package/dist/commands/agents/tools/auth.js +27 -1
- package/dist/commands/agents/tools/index.js +11 -1
- package/dist/commands/agents/update.js +6 -1
- package/dist/commands/code/install.d.ts +4 -2
- package/dist/commands/code/install.js +12 -10
- package/dist/commands/convos/list.js +1 -3
- package/dist/commands/convos/show.js +4 -11
- package/dist/commands/daemon/index.js +3 -3
- package/dist/commands/daemon/install.js +4 -4
- package/dist/commands/daemon/login.js +1 -1
- package/dist/commands/daemon/logs.d.ts +3 -1
- package/dist/commands/daemon/logs.js +16 -1
- package/dist/commands/daemon/start.js +3 -3
- package/dist/commands/daemon/status.d.ts +9 -0
- package/dist/commands/daemon/status.js +22 -0
- package/dist/commands/dm/messages.js +1 -11
- package/dist/commands/followups/cancel.js +1 -1
- package/dist/commands/followups/resolve.js +1 -1
- package/dist/commands/followups/show.js +16 -0
- package/dist/commands/harnesses/forget.js +20 -3
- package/dist/commands/harnesses/install.js +18 -6
- package/dist/commands/harnesses/installers.d.ts +1 -1
- package/dist/commands/harnesses/installers.js +36 -12
- package/dist/commands/harnesses/list.d.ts +74 -2
- package/dist/commands/harnesses/list.js +231 -15
- package/dist/commands/harnesses/ping.d.ts +2 -0
- package/dist/commands/harnesses/ping.js +21 -0
- package/dist/commands/harnesses/show.d.ts +22 -0
- package/dist/commands/harnesses/show.js +116 -17
- package/dist/commands/harnesses/update.d.ts +8 -0
- package/dist/commands/harnesses/update.js +25 -1
- package/dist/commands/harnesses/usage.d.ts +2 -0
- package/dist/commands/harnesses/usage.js +7 -2
- package/dist/commands/instructions/status.js +13 -0
- package/dist/commands/instructions/update-target.d.ts +1 -0
- package/dist/commands/instructions/update-target.js +11 -3
- package/dist/commands/login.js +5 -5
- package/dist/commands/machines/list.js +8 -9
- package/dist/commands/machines/show.js +46 -17
- package/dist/commands/messages/show.js +3 -10
- package/dist/commands/shares/get.js +2 -9
- package/dist/commands/shares/list.js +1 -9
- package/dist/commands/shortcuts/show.js +4 -11
- package/dist/commands/skills/local.js +6 -0
- package/dist/commands/spaces/actions/run.js +3 -4
- package/dist/commands/spaces/briefs/get.js +2 -9
- package/dist/commands/spaces/briefs/list.js +1 -9
- package/dist/commands/spaces/conversations.js +1 -9
- package/dist/commands/spaces/heartbeats/list.js +1 -9
- package/dist/commands/spaces/heartbeats/status.js +2 -9
- package/dist/commands/tasks/activity.js +1 -10
- package/dist/commands/tasks/approvals/list.js +1 -9
- package/dist/commands/tasks/attachments/list.js +1 -9
- package/dist/commands/tasks/comments/list.js +1 -9
- package/dist/commands/tasks/dri-status.js +2 -9
- package/dist/commands/tasks/events/append.js +9 -7
- package/dist/commands/tasks/execution-timeline.js +1 -9
- package/dist/commands/tasks/monitor.js +1 -9
- package/dist/commands/tasks/result/submit.js +9 -0
- package/dist/commands/tasks/runs.js +1 -9
- package/dist/commands/tasks/show.js +5 -12
- package/dist/commands/token/list.d.ts +8 -0
- package/dist/commands/token/list.js +51 -6
- package/dist/help.js +8 -12
- package/dist/lib/agent-home-workspace.d.ts +19 -0
- package/dist/lib/agent-home-workspace.js +39 -0
- package/dist/lib/agent-resolver.js +2 -2
- package/dist/lib/agentic-stream.d.ts +22 -1
- package/dist/lib/agentic-stream.js +48 -7
- package/dist/lib/cli-installers.d.ts +84 -2
- package/dist/lib/cli-installers.js +146 -13
- package/dist/lib/command-miss.d.ts +22 -0
- package/dist/lib/command-miss.js +78 -0
- package/dist/lib/daemon-setup.js +2 -2
- package/dist/lib/daemon-target.d.ts +14 -0
- package/dist/lib/daemon-target.js +32 -0
- package/dist/lib/daemonBroker.js +1 -1
- package/dist/lib/daemonHandoff.js +1 -1
- package/dist/lib/exec-runtime-binary.js +2 -2
- package/dist/lib/followups.d.ts +10 -0
- package/dist/lib/followups.js +14 -1
- package/dist/lib/format.d.ts +11 -0
- package/dist/lib/format.js +21 -0
- package/dist/lib/harness-labels.d.ts +39 -0
- package/dist/lib/harness-labels.js +60 -0
- package/dist/lib/harnesses.d.ts +6 -0
- package/dist/lib/instruction-input.d.ts +13 -0
- package/dist/lib/instruction-input.js +20 -1
- package/dist/lib/local-skills.d.ts +17 -1
- package/dist/lib/local-skills.js +20 -2
- package/dist/lib/login.js +5 -5
- package/dist/lib/machines.d.ts +6 -0
- package/dist/lib/machines.js +68 -2
- package/dist/lib/runtime-tool-support.d.ts +34 -0
- package/dist/lib/runtime-tool-support.js +77 -0
- package/dist/lib/share-rows.d.ts +17 -0
- package/dist/lib/share-rows.js +35 -0
- package/dist/node_modules/@skrr-ai/data-provider/index.js +5029 -5007
- package/oclif.manifest.json +2689 -2584
- package/package.json +6 -3
package/dist/base-command.d.ts
CHANGED
|
@@ -338,6 +338,15 @@ export interface ParsedGlobals {
|
|
|
338
338
|
workspace?: string;
|
|
339
339
|
}
|
|
340
340
|
export declare function parseGlobalFlags(argv: readonly string[]): ParsedGlobals;
|
|
341
|
+
/**
|
|
342
|
+
* The typed `code` and message an API refusal carried, read the way
|
|
343
|
+
* `handleApiError` reads them, for a command that answers one code itself
|
|
344
|
+
* before falling back to the uniform translation.
|
|
345
|
+
*/
|
|
346
|
+
export declare function apiRefusal(err: unknown): {
|
|
347
|
+
code?: string;
|
|
348
|
+
message?: string;
|
|
349
|
+
};
|
|
341
350
|
/**
|
|
342
351
|
* Prefix a failure message with the resource it is about.
|
|
343
352
|
*
|
package/dist/base-command.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Flags = exports.BaseCommand = exports.resolveProfileFromArgv = void 0;
|
|
4
4
|
exports.assigneePayloadNeedsResolution = assigneePayloadNeedsResolution;
|
|
5
5
|
exports.parseGlobalFlags = parseGlobalFlags;
|
|
6
|
+
exports.apiRefusal = apiRefusal;
|
|
6
7
|
exports.withSubject = withSubject;
|
|
7
8
|
exports.describeHarnessUnavailable = describeHarnessUnavailable;
|
|
8
9
|
exports.describeGitHubUnavailable = describeGitHubUnavailable;
|
|
@@ -309,7 +310,7 @@ class BaseCommand extends core_1.Command {
|
|
|
309
310
|
// to log in again. Reachable in the ordinary way — a cold machine
|
|
310
311
|
// running several skrr commands at once has every process brokering,
|
|
311
312
|
// because none of them has a token yet.
|
|
312
|
-
process.stderr.write('[skrr] The
|
|
313
|
+
process.stderr.write('[skrr] The skrr background service on this computer is being rate-limited while brokering logins. ' +
|
|
313
314
|
'This is transient — retry in a few minutes, or run `skrr login` to sign in directly.\n');
|
|
314
315
|
}
|
|
315
316
|
else if (autoResult.outcome.reason === 'base_url_mismatch' &&
|
|
@@ -317,7 +318,7 @@ class BaseCommand extends core_1.Command {
|
|
|
317
318
|
// The user is about to see "Not signed in" — give them the
|
|
318
319
|
// exact fix. One stderr line so it doesn't pollute --json
|
|
319
320
|
// consumers on stdout.
|
|
320
|
-
process.stderr.write(`[skrr]
|
|
321
|
+
process.stderr.write(`[skrr] The skrr background service on this computer is logged in to ${autoResult.outcome.daemonServerUrl}, ` +
|
|
321
322
|
`but skrr is configured for ${this.cliConfig.baseURL}. ` +
|
|
322
323
|
`Run \`skrr login --base-url ${autoResult.outcome.daemonServerUrl}\` to use the daemon.\n`);
|
|
323
324
|
}
|
|
@@ -1355,6 +1356,20 @@ function stringProp(obj, key) {
|
|
|
1355
1356
|
const value = obj?.[key];
|
|
1356
1357
|
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
1357
1358
|
}
|
|
1359
|
+
/**
|
|
1360
|
+
* The typed `code` and message an API refusal carried, read the way
|
|
1361
|
+
* `handleApiError` reads them, for a command that answers one code itself
|
|
1362
|
+
* before falling back to the uniform translation.
|
|
1363
|
+
*/
|
|
1364
|
+
function apiRefusal(err) {
|
|
1365
|
+
if (!(err instanceof Error))
|
|
1366
|
+
return {};
|
|
1367
|
+
const body = parseErrorBody(err.body);
|
|
1368
|
+
return {
|
|
1369
|
+
code: stringProp(body, 'code') || stringProp(body, 'errorType'),
|
|
1370
|
+
message: stringProp(body, 'message') || stringProp(body, 'error'),
|
|
1371
|
+
};
|
|
1372
|
+
}
|
|
1358
1373
|
/**
|
|
1359
1374
|
* Prefix a failure message with the resource it is about.
|
|
1360
1375
|
*
|
|
@@ -1622,7 +1637,7 @@ function describeDaemonScopedCredential(bin, scope = 'daemon', source) {
|
|
|
1622
1637
|
? `The stored credential has ${scope === 'daemon-install' ? 'daemon-install' : 'daemon'} scope`
|
|
1623
1638
|
: `This command received a ${scopeLabel} credential`;
|
|
1624
1639
|
return (`${subject}, which cannot access user API commands. ` +
|
|
1625
|
-
`Run \`${bin} login\` to create a CLI credential. For the
|
|
1640
|
+
`Run \`${bin} login\` to create a CLI credential. For the background service on this computer, use \`${bin} daemon login\`.`);
|
|
1626
1641
|
}
|
|
1627
1642
|
/**
|
|
1628
1643
|
* Did the server MEAN this 5xx, or did it fall over?
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const format_1 = require("../../lib/format");
|
|
7
|
+
const share_rows_1 = require("../../lib/share-rows");
|
|
7
8
|
class AgentSharesListFor extends base_command_1.BaseCommand {
|
|
8
9
|
static description = 'List all users an agent has been shared with';
|
|
9
10
|
static examples = [
|
|
@@ -37,10 +38,10 @@ class AgentSharesListFor extends base_command_1.BaseCommand {
|
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
const rows = items.map((s) => ({
|
|
40
|
-
shareId:
|
|
41
|
-
recipient:
|
|
41
|
+
shareId: (0, share_rows_1.shareIdOf)(s),
|
|
42
|
+
recipient: (0, share_rows_1.sharePartyLabel)(s.recipient ?? s.recipientId ?? s.to),
|
|
42
43
|
status: String(s.status ?? '-'),
|
|
43
|
-
sharedAt:
|
|
44
|
+
sharedAt: (0, format_1.localTime)(s.createdAt ?? s.sharedAt),
|
|
44
45
|
}));
|
|
45
46
|
(0, format_1.renderTable)(rows, [
|
|
46
47
|
{ key: 'shareId', header: 'SHARE ID' },
|
|
@@ -56,11 +57,3 @@ class AgentSharesListFor extends base_command_1.BaseCommand {
|
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
59
|
exports.default = AgentSharesListFor;
|
|
59
|
-
function formatTime(value) {
|
|
60
|
-
try {
|
|
61
|
-
return new Date(String(value)).toISOString().slice(0, 19).replace('T', ' ');
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return String(value ?? '-');
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const format_1 = require("../../lib/format");
|
|
7
|
+
const share_rows_1 = require("../../lib/share-rows");
|
|
7
8
|
class AgentSharesReceived extends base_command_1.BaseCommand {
|
|
8
9
|
static description = 'List agent shares received by the current user';
|
|
9
10
|
static examples = [
|
|
@@ -44,12 +45,12 @@ class AgentSharesReceived extends base_command_1.BaseCommand {
|
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
const rows = items.map((s) => ({
|
|
47
|
-
shareId:
|
|
48
|
+
shareId: (0, share_rows_1.shareIdOf)(s),
|
|
48
49
|
agentId: String(s.agentId ?? '-'),
|
|
49
50
|
agentName: String(s.agentName ?? s.agent?.name ?? '-'),
|
|
50
|
-
from:
|
|
51
|
+
from: (0, share_rows_1.sharePartyLabel)(s.owner ?? s.senderId ?? s.from),
|
|
51
52
|
status: String(s.status ?? '-'),
|
|
52
|
-
sharedAt:
|
|
53
|
+
sharedAt: (0, format_1.localTime)(s.createdAt ?? s.sharedAt),
|
|
53
54
|
}));
|
|
54
55
|
(0, format_1.renderTable)(rows, [
|
|
55
56
|
{ key: 'shareId', header: 'SHARE ID' },
|
|
@@ -67,11 +68,3 @@ class AgentSharesReceived extends base_command_1.BaseCommand {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
exports.default = AgentSharesReceived;
|
|
70
|
-
function formatTime(value) {
|
|
71
|
-
try {
|
|
72
|
-
return new Date(String(value)).toISOString().slice(0, 19).replace('T', ' ');
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return String(value ?? '-');
|
|
76
|
-
}
|
|
77
|
-
}
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const format_1 = require("../../lib/format");
|
|
7
|
+
const share_rows_1 = require("../../lib/share-rows");
|
|
7
8
|
class AgentSharesSent extends base_command_1.BaseCommand {
|
|
8
9
|
static description = 'List agent shares sent by the current user';
|
|
9
10
|
static examples = [
|
|
@@ -41,12 +42,12 @@ class AgentSharesSent extends base_command_1.BaseCommand {
|
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
43
44
|
const rows = items.map((s) => ({
|
|
44
|
-
shareId:
|
|
45
|
+
shareId: (0, share_rows_1.shareIdOf)(s),
|
|
45
46
|
agentId: String(s.agentId ?? '-'),
|
|
46
47
|
agentName: String(s.agentName ?? s.agent?.name ?? '-'),
|
|
47
|
-
to:
|
|
48
|
+
to: (0, share_rows_1.sharePartyLabel)(s.recipient ?? s.recipientId ?? s.to),
|
|
48
49
|
status: String(s.status ?? '-'),
|
|
49
|
-
sharedAt:
|
|
50
|
+
sharedAt: (0, format_1.localTime)(s.createdAt ?? s.sharedAt),
|
|
50
51
|
}));
|
|
51
52
|
(0, format_1.renderTable)(rows, [
|
|
52
53
|
{ key: 'shareId', header: 'SHARE ID' },
|
|
@@ -64,11 +65,3 @@ class AgentSharesSent extends base_command_1.BaseCommand {
|
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
exports.default = AgentSharesSent;
|
|
67
|
-
function formatTime(value) {
|
|
68
|
-
try {
|
|
69
|
-
return new Date(String(value)).toISOString().slice(0, 19).replace('T', ' ');
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
return String(value ?? '-');
|
|
73
|
-
}
|
|
74
|
-
}
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const format_1 = require("../../lib/format");
|
|
7
|
+
const share_rows_1 = require("../../lib/share-rows");
|
|
7
8
|
class AgentSharesWith extends base_command_1.BaseCommand {
|
|
8
9
|
static description = 'Show all agent shares with a specific user';
|
|
9
10
|
static examples = [
|
|
@@ -39,11 +40,11 @@ class AgentSharesWith extends base_command_1.BaseCommand {
|
|
|
39
40
|
return;
|
|
40
41
|
}
|
|
41
42
|
const toRows = (items) => items.map((s) => ({
|
|
42
|
-
shareId:
|
|
43
|
+
shareId: (0, share_rows_1.shareIdOf)(s),
|
|
43
44
|
agentId: String(s.agentId ?? '-'),
|
|
44
45
|
agentName: String(s.agentName ?? s.agent?.name ?? '-'),
|
|
45
46
|
status: String(s.status ?? '-'),
|
|
46
|
-
sharedAt:
|
|
47
|
+
sharedAt: (0, format_1.localTime)(s.createdAt ?? s.sharedAt),
|
|
47
48
|
}));
|
|
48
49
|
const columns = [
|
|
49
50
|
{ key: 'shareId', header: 'SHARE ID' },
|
|
@@ -69,11 +70,3 @@ class AgentSharesWith extends base_command_1.BaseCommand {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
exports.default = AgentSharesWith;
|
|
72
|
-
function formatTime(value) {
|
|
73
|
-
try {
|
|
74
|
-
return new Date(String(value)).toISOString().slice(0, 19).replace('T', ' ');
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return String(value ?? '-');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
@@ -162,7 +162,7 @@ class AgentsActivity extends base_command_1.BaseCommand {
|
|
|
162
162
|
}
|
|
163
163
|
else {
|
|
164
164
|
(0, format_1.renderTable)(entries.map((entry) => ({
|
|
165
|
-
when:
|
|
165
|
+
when: (0, format_1.localTime)(entry.at),
|
|
166
166
|
kind: entry.kind,
|
|
167
167
|
title: (0, format_1.truncate)(entry.title, 50),
|
|
168
168
|
detail: (0, format_1.truncate)(entry.detail, 40),
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const promises_1 = require("node:fs/promises");
|
|
5
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
|
+
const runtime_tool_support_1 = require("../../../lib/runtime-tool-support");
|
|
6
7
|
const base_command_1 = require("../../../base-command");
|
|
7
8
|
/**
|
|
8
9
|
* Attach (or re-attach) an OpenAPI document to an agent as a tool surface.
|
|
@@ -199,6 +200,11 @@ class AgentsApiActionsAdd extends base_command_1.BaseCommand {
|
|
|
199
200
|
}
|
|
200
201
|
this.log(`${flags['action-id'] ? 'Updated' : 'Added'} an OpenAPI action on agent ${args.agentId} — ` +
|
|
201
202
|
`${functions.length} operation(s) on ${domain}`);
|
|
203
|
+
const actionId = result[1]?.action_id;
|
|
204
|
+
if (flags.auth === data_provider_1.AuthTypeEnum.OAuth && actionId) {
|
|
205
|
+
const redirectUrl = new URL(`/api/actions/${encodeURIComponent(actionId)}/oauth/callback`, this.cliConfig.baseURL).toString();
|
|
206
|
+
this.log((0, runtime_tool_support_1.oauthActionNotice)(redirectUrl));
|
|
207
|
+
}
|
|
202
208
|
this.printAgentUrl(args.agentId);
|
|
203
209
|
}
|
|
204
210
|
}
|
|
@@ -12,6 +12,7 @@ const node_fs_1 = require("node:fs");
|
|
|
12
12
|
const core_1 = require("@oclif/core");
|
|
13
13
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
14
14
|
const base_command_1 = require("../../base-command");
|
|
15
|
+
const agent_home_workspace_1 = require("../../lib/agent-home-workspace");
|
|
15
16
|
const agentic_stream_1 = require("../../lib/agentic-stream");
|
|
16
17
|
const web_url_1 = require("../../lib/web-url");
|
|
17
18
|
const local_skills_1 = require("../../lib/local-skills");
|
|
@@ -302,7 +303,10 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
302
303
|
exit: 2,
|
|
303
304
|
});
|
|
304
305
|
}
|
|
305
|
-
const { refs, problems } = (0, local_skills_1.resolveLocalSkillRefs)(inventory.skills ?? [], requested
|
|
306
|
+
const { refs, problems } = (0, local_skills_1.resolveLocalSkillRefs)(inventory.skills ?? [], requested, {
|
|
307
|
+
snapshot: inventory.cwdResolved !== true,
|
|
308
|
+
daemonId,
|
|
309
|
+
});
|
|
306
310
|
if (problems.length > 0) {
|
|
307
311
|
this.error(problems.join('\n'), { exit: 2 });
|
|
308
312
|
}
|
|
@@ -446,6 +450,7 @@ class AgentsChat extends base_command_1.BaseCommand {
|
|
|
446
450
|
const prompt = localSkills
|
|
447
451
|
? (0, local_skills_1.withSkillInvocations)(flags.prompt, localSkills.refs)
|
|
448
452
|
: flags.prompt;
|
|
453
|
+
await (0, agent_home_workspace_1.applyAgentHomeWorkspaceDefault)(args.agentId, (agentId) => data_provider_1.dataService.getAgentById({ agent_id: agentId }));
|
|
449
454
|
let started;
|
|
450
455
|
try {
|
|
451
456
|
started = await data_provider_1.dataService.startAgenticChat({
|
|
@@ -8,6 +8,7 @@ exports.backgroundLoopLines = backgroundLoopLines;
|
|
|
8
8
|
const core_1 = require("@oclif/core");
|
|
9
9
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
10
10
|
const agent_visual_refs_1 = require("../../lib/agent-visual-refs");
|
|
11
|
+
const runtime_tool_support_1 = require("../../lib/runtime-tool-support");
|
|
11
12
|
const base_command_1 = require("../../base-command");
|
|
12
13
|
const execution_target_1 = require("../../lib/execution-target");
|
|
13
14
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -201,7 +202,10 @@ class AgentsCreate extends base_command_1.BaseCommand {
|
|
|
201
202
|
description: 'Default Claude permission mode for sessions',
|
|
202
203
|
options: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
203
204
|
}),
|
|
204
|
-
tools: core_1.Flags.string({
|
|
205
|
+
tools: core_1.Flags.string({
|
|
206
|
+
description: 'Tools JSON array of plugin keys, e.g. \'["calculator"]\'. `skrr agents tools` lists the keys, ' +
|
|
207
|
+
'and which of them an agent turn can call.',
|
|
208
|
+
}),
|
|
205
209
|
'model-parameters': core_1.Flags.string({ description: 'Model parameters JSON object' }),
|
|
206
210
|
'agent-ids': core_1.Flags.string({ description: 'Delegated agent IDs JSON array' }),
|
|
207
211
|
'network-mode': core_1.Flags.string({ description: 'Agent network mode' }),
|
|
@@ -382,6 +386,7 @@ class AgentsCreate extends base_command_1.BaseCommand {
|
|
|
382
386
|
if (unapplied.length > 0) {
|
|
383
387
|
this.warn(unappliedPayloadKeysWarning(unapplied));
|
|
384
388
|
}
|
|
389
|
+
await (0, runtime_tool_support_1.warnPluginsNotLoaded)(payload.tools, (message) => this.warn(message));
|
|
385
390
|
if (flags.json) {
|
|
386
391
|
this.log(JSON.stringify((0, web_url_1.withAgentUrl)(this.cliConfig.baseURL, created), null, 2));
|
|
387
392
|
return;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* Create or repair this account's Agent Manager (the HR Agent).
|
|
4
|
+
*
|
|
5
|
+
* Wraps `POST /api/agents/manager/ensure`. New accounts get an HR Agent at
|
|
6
|
+
* signup; accounts that predate it had no way to get one (OSK-9745), and a
|
|
7
|
+
* signup whose provisioning failed part-way was never finished (OSK-9785).
|
|
8
|
+
* IDEMPOTENT: run it again and it reports the existing agent, or what it had
|
|
9
|
+
* to repair.
|
|
10
|
+
*/
|
|
11
|
+
export default class AgentsManagerEnsure extends BaseCommand {
|
|
12
|
+
static description: string;
|
|
13
|
+
static examples: string[];
|
|
14
|
+
static flags: {
|
|
15
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
run(): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
const web_url_1 = require("../../../lib/web-url");
|
|
7
|
+
/**
|
|
8
|
+
* Create or repair this account's Agent Manager (the HR Agent).
|
|
9
|
+
*
|
|
10
|
+
* Wraps `POST /api/agents/manager/ensure`. New accounts get an HR Agent at
|
|
11
|
+
* signup; accounts that predate it had no way to get one (OSK-9745), and a
|
|
12
|
+
* signup whose provisioning failed part-way was never finished (OSK-9785).
|
|
13
|
+
* IDEMPOTENT: run it again and it reports the existing agent, or what it had
|
|
14
|
+
* to repair.
|
|
15
|
+
*/
|
|
16
|
+
class AgentsManagerEnsure extends base_command_1.BaseCommand {
|
|
17
|
+
static description = "Create your Agent Manager (HR Agent) if you don't have one, or repair it if it is incomplete";
|
|
18
|
+
static examples = [
|
|
19
|
+
'<%= config.bin %> agents manager ensure',
|
|
20
|
+
'<%= config.bin %> agents manager ensure --workspace <workspace-id> --json',
|
|
21
|
+
];
|
|
22
|
+
static flags = {
|
|
23
|
+
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
24
|
+
};
|
|
25
|
+
async run() {
|
|
26
|
+
this.requireAuth();
|
|
27
|
+
const { flags } = await this.parse(AgentsManagerEnsure);
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await data_provider_1.dataService.ensureAgentManager();
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
this.handleApiError(err);
|
|
34
|
+
}
|
|
35
|
+
const agent = (0, web_url_1.withAgentUrl)(this.cliConfig.baseURL, {
|
|
36
|
+
...result.agent,
|
|
37
|
+
});
|
|
38
|
+
if (flags.json) {
|
|
39
|
+
this.log(JSON.stringify({ ...result, agent }, null, 2));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const label = `${result.agent.id} — ${result.agent.name}`;
|
|
43
|
+
if (result.created) {
|
|
44
|
+
this.log(`Created your Agent Manager: ${label}`);
|
|
45
|
+
}
|
|
46
|
+
else if (result.repaired.length > 0) {
|
|
47
|
+
this.log(`Repaired your Agent Manager (${result.repaired.join(', ')}): ${label}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
this.log(`You already have an Agent Manager: ${label}`);
|
|
51
|
+
}
|
|
52
|
+
this.log(`Workspace: ${result.workspaceId}`);
|
|
53
|
+
this.log(`URL: ${agent.url ?? '-'}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
exports.default = AgentsManagerEnsure;
|
|
@@ -179,6 +179,10 @@ class AgentsShow extends base_command_1.BaseCommand {
|
|
|
179
179
|
}
|
|
180
180
|
const tools = Array.isArray(a.tools) ? a.tools : [];
|
|
181
181
|
this.log(`Tools: ${tools.length === 0 ? '(none)' : tools.join(', ')}`);
|
|
182
|
+
if (tools.length > 0) {
|
|
183
|
+
// Stored, not loaded: the runtime has none of these yet (OSK-9750, OSK-9754).
|
|
184
|
+
this.log(' (saved on the agent; the agent runtime does not load these yet)');
|
|
185
|
+
}
|
|
182
186
|
if (a.description) {
|
|
183
187
|
this.log('');
|
|
184
188
|
this.log('Description:');
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* A credential's help text for a terminal. Plugin manifests write it as HTML
|
|
4
|
+
* with the signup link in an anchor; stripping the tags alone would keep
|
|
5
|
+
* "see Our Docs" and drop the URL, which is the part worth printing.
|
|
6
|
+
*/
|
|
7
|
+
export declare function credentialHelpText(description: string): string;
|
|
2
8
|
/**
|
|
3
9
|
* Whether this account has credentials for one tool.
|
|
4
10
|
*
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.credentialHelpText = credentialHelpText;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
6
|
const base_command_1 = require("../../../base-command");
|
|
7
|
+
const html_text_1 = require("../../../lib/html-text");
|
|
8
|
+
/**
|
|
9
|
+
* A credential's help text for a terminal. Plugin manifests write it as HTML
|
|
10
|
+
* with the signup link in an anchor; stripping the tags alone would keep
|
|
11
|
+
* "see Our Docs" and drop the URL, which is the part worth printing.
|
|
12
|
+
*/
|
|
13
|
+
function credentialHelpText(description) {
|
|
14
|
+
return (0, html_text_1.htmlToDisplayText)(description.replace(/<a\s[^>]*href=['"]([^'"]+)['"][^>]*>(.*?)<\/a>/gi, '$2 ($1)'));
|
|
15
|
+
}
|
|
6
16
|
/**
|
|
7
17
|
* Whether this account has credentials for one tool.
|
|
8
18
|
*
|
|
@@ -33,9 +43,25 @@ class AgentsToolsAuth extends base_command_1.BaseCommand {
|
|
|
33
43
|
this.log(JSON.stringify(result, null, 2));
|
|
34
44
|
return;
|
|
35
45
|
}
|
|
46
|
+
const missing = result.missing ?? [];
|
|
36
47
|
this.log(result.authenticated
|
|
37
48
|
? `${args.toolId}: authenticated`
|
|
38
|
-
: `${args.toolId}: not authenticated${result.message ? ` — ${result.message}` : ''}`);
|
|
49
|
+
: `${args.toolId}: not authenticated${missing.length === 0 && result.message ? ` — ${result.message}` : ''}`);
|
|
50
|
+
// The answer's next question is "what do I supply, and where do I get it"
|
|
51
|
+
// (OSK-9550); the manifest carries both, so print them.
|
|
52
|
+
if (missing.length > 0) {
|
|
53
|
+
this.log('Missing credentials:');
|
|
54
|
+
for (const credential of missing) {
|
|
55
|
+
this.log(` ${credential.authField} ${credential.label ?? ''}`.trimEnd());
|
|
56
|
+
if (credential.description) {
|
|
57
|
+
this.log(` ${credentialHelpText(credential.description)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
this.log('Add them where you add this tool to an agent in the web app.');
|
|
61
|
+
}
|
|
62
|
+
if (result.agentRuntime === false) {
|
|
63
|
+
this.log(`Agent turns cannot call ${args.toolId}: it saves output through a web request an agent turn does not have.`);
|
|
64
|
+
}
|
|
39
65
|
}
|
|
40
66
|
}
|
|
41
67
|
exports.default = AgentsToolsAuth;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const runtime_tool_support_1 = require("../../../lib/runtime-tool-support");
|
|
5
6
|
const base_command_1 = require("../../../base-command");
|
|
6
7
|
const format_1 = require("../../../lib/format");
|
|
7
8
|
/**
|
|
@@ -63,15 +64,24 @@ class AgentsToolsList extends base_command_1.BaseCommand {
|
|
|
63
64
|
pluginKey: tool.pluginKey,
|
|
64
65
|
name: tool.name,
|
|
65
66
|
auth: authState(tool),
|
|
67
|
+
agent: (0, runtime_tool_support_1.agentRuntimeLabel)(tool),
|
|
66
68
|
description: String(tool.description ?? ''),
|
|
67
69
|
})), [
|
|
68
70
|
{ key: 'pluginKey', header: 'PLUGIN KEY', verbatim: true },
|
|
69
71
|
{ key: 'name', header: 'NAME', maxWidth: 28 },
|
|
70
72
|
{ key: 'auth', header: 'AUTH' },
|
|
73
|
+
{ key: 'agent', header: 'AGENT' },
|
|
71
74
|
{ key: 'description', header: 'DESCRIPTION', maxWidth: 52 },
|
|
72
75
|
], (line) => this.log(line));
|
|
73
76
|
this.log('');
|
|
74
|
-
|
|
77
|
+
// The example must be a key this list printed (OSK-9600: it was web_search),
|
|
78
|
+
// preferably one that works without credentials.
|
|
79
|
+
const example = rows.find((tool) => !tool.authConfig?.length) ?? rows[0];
|
|
80
|
+
this.log(`PLUGIN KEY is what --tools stores, e.g. --tools '["${example.pluginKey}"]'.`);
|
|
81
|
+
this.log(runtime_tool_support_1.AGENT_COLUMN_FOOTNOTE);
|
|
82
|
+
if (rows.some((tool) => tool.authConfig?.length && !tool.authenticated)) {
|
|
83
|
+
this.log('`skrr agents tools auth <plugin-key>` names the credentials a tool still needs.');
|
|
84
|
+
}
|
|
75
85
|
}
|
|
76
86
|
}
|
|
77
87
|
exports.default = AgentsToolsList;
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const agent_visual_refs_1 = require("../../lib/agent-visual-refs");
|
|
6
6
|
const create_1 = require("./create");
|
|
7
|
+
const runtime_tool_support_1 = require("../../lib/runtime-tool-support");
|
|
7
8
|
const base_command_1 = require("../../base-command");
|
|
8
9
|
const execution_target_1 = require("../../lib/execution-target");
|
|
9
10
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -51,7 +52,10 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
51
52
|
description: 'Default Claude permission mode for sessions',
|
|
52
53
|
options: ['default', 'acceptEdits', 'bypassPermissions', 'plan'],
|
|
53
54
|
}),
|
|
54
|
-
tools: core_1.Flags.string({
|
|
55
|
+
tools: core_1.Flags.string({
|
|
56
|
+
description: 'Tools JSON array of plugin keys, e.g. \'["calculator"]\'. `skrr agents tools` lists the keys, ' +
|
|
57
|
+
'and which of them an agent turn can call.',
|
|
58
|
+
}),
|
|
55
59
|
'model-parameters': core_1.Flags.string({ description: 'Model parameters JSON object' }),
|
|
56
60
|
'agent-ids': core_1.Flags.string({ description: 'Delegated agent IDs JSON array' }),
|
|
57
61
|
'network-mode': core_1.Flags.string({ description: 'Agent network mode' }),
|
|
@@ -195,6 +199,7 @@ class AgentsUpdate extends base_command_1.BaseCommand {
|
|
|
195
199
|
if (unapplied.length > 0) {
|
|
196
200
|
this.warn((0, create_1.unappliedPayloadKeysWarning)(unapplied));
|
|
197
201
|
}
|
|
202
|
+
await (0, runtime_tool_support_1.warnPluginsNotLoaded)(payload.tools, (message) => this.warn(message));
|
|
198
203
|
if (flags.json) {
|
|
199
204
|
this.log(JSON.stringify((0, web_url_1.withAgentUrl)(this.cliConfig.baseURL, updated), null, 2));
|
|
200
205
|
return;
|
|
@@ -38,8 +38,10 @@ export default class CodeInstall extends BaseCommand {
|
|
|
38
38
|
*
|
|
39
39
|
* Refuses rather than guessing when several are active. Picking one would mean
|
|
40
40
|
* installing a hundred megabytes onto a machine the user was not thinking
|
|
41
|
-
* about
|
|
42
|
-
* so the error
|
|
41
|
+
* about. The candidates are CONNECTED daemons by the id the installer routes
|
|
42
|
+
* take (`listConnectedDaemons`), so the error names values that work — the
|
|
43
|
+
* Daemon resource list `skrr list-daemons` prints is a different id space
|
|
44
|
+
* (OSK-9993).
|
|
43
45
|
*
|
|
44
46
|
* A Dedicated Runtime's own machine is refused here, before any request. The
|
|
45
47
|
* daemon and the API refuse it too; saying so locally is what keeps the answer
|
|
@@ -34,7 +34,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
34
34
|
static description = `Install the ${auth_core_1.FIRST_PARTY_HARNESS.displayName} engine on this machine, through the daemon`;
|
|
35
35
|
static examples = [
|
|
36
36
|
'<%= config.bin %> code install',
|
|
37
|
-
'<%= config.bin %> code install --daemon
|
|
37
|
+
'<%= config.bin %> code install --daemon <daemon-id>',
|
|
38
38
|
'<%= config.bin %> code install --json',
|
|
39
39
|
];
|
|
40
40
|
static flags = {
|
|
@@ -90,7 +90,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
90
90
|
this.failWithCliError({
|
|
91
91
|
message: `The daemon on ${(0, daemon_ref_1.formatDaemonRef)(daemonId)} accepted the request and returned no ` +
|
|
92
92
|
'install job, and does not say why. Nothing has been shown to start. ' +
|
|
93
|
-
`Check what it offers with \`${this.config.bin}
|
|
93
|
+
`Check what it offers with \`${this.config.bin} harnesses installers --daemon ${daemonId}\`, and ` +
|
|
94
94
|
`\`${this.config.bin} code doctor\` here once you expect it to have landed.`,
|
|
95
95
|
code: 'INSTALL_NOT_TRACKED',
|
|
96
96
|
exit: 1,
|
|
@@ -132,7 +132,7 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
132
132
|
return await (0, daemon_target_1.resolveDaemonTarget)({
|
|
133
133
|
explicit,
|
|
134
134
|
local: (0, daemon_target_1.localMachine)({ baseURL: this.cliConfig?.baseURL }),
|
|
135
|
-
listActiveDaemons: () => (0, daemon_target_1.
|
|
135
|
+
listActiveDaemons: () => (0, daemon_target_1.listConnectedDaemons)(this.resolvedCredential),
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
138
|
catch (err) {
|
|
@@ -144,8 +144,10 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
144
144
|
*
|
|
145
145
|
* Refuses rather than guessing when several are active. Picking one would mean
|
|
146
146
|
* installing a hundred megabytes onto a machine the user was not thinking
|
|
147
|
-
* about
|
|
148
|
-
* so the error
|
|
147
|
+
* about. The candidates are CONNECTED daemons by the id the installer routes
|
|
148
|
+
* take (`listConnectedDaemons`), so the error names values that work — the
|
|
149
|
+
* Daemon resource list `skrr list-daemons` prints is a different id space
|
|
150
|
+
* (OSK-9993).
|
|
149
151
|
*
|
|
150
152
|
* A Dedicated Runtime's own machine is refused here, before any request. The
|
|
151
153
|
* daemon and the API refuse it too; saying so locally is what keeps the answer
|
|
@@ -169,8 +171,8 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
169
171
|
}
|
|
170
172
|
if (target.kind === 'none') {
|
|
171
173
|
this.failWithCliError({
|
|
172
|
-
message: 'No
|
|
173
|
-
`Start the
|
|
174
|
+
message: 'No daemon is connected for this account, so nothing can perform the install. ' +
|
|
175
|
+
`Start the background service on this computer with \`${this.config.bin} daemon start\`, then run this again.`,
|
|
174
176
|
code: 'NO_DAEMON',
|
|
175
177
|
exit: 1,
|
|
176
178
|
retryable: false,
|
|
@@ -179,11 +181,11 @@ class CodeInstall extends base_command_1.BaseCommand {
|
|
|
179
181
|
const { daemons } = target;
|
|
180
182
|
const choices = daemons
|
|
181
183
|
.slice(0, 10)
|
|
182
|
-
.map((d) => ` ${
|
|
184
|
+
.map((d) => ` ${d.id} ${d.name || '—'}`)
|
|
183
185
|
.join('\n');
|
|
184
186
|
this.failWithCliError({
|
|
185
|
-
message: `${daemons.length}
|
|
186
|
-
`Pass one with --daemon, or see
|
|
187
|
+
message: `${daemons.length} connected daemons — say which machine should install the engine:\n${choices}\n` +
|
|
188
|
+
`Pass one with --daemon, or see each daemon id in the DAEMON ID column of \`${this.config.bin} harnesses list\`.`,
|
|
187
189
|
code: 'AMBIGUOUS_DAEMON',
|
|
188
190
|
exit: 1,
|
|
189
191
|
retryable: false,
|
|
@@ -110,9 +110,7 @@ class ConvosList extends base_command_1.BaseCommand {
|
|
|
110
110
|
// The owning agent is the field callers most often had to reach for --json
|
|
111
111
|
// to see (and the one `--agent` filters on), so surface it in the table too.
|
|
112
112
|
agent: String(c.agent_id ?? c.agentId ?? '-'),
|
|
113
|
-
updated: c.updatedAt
|
|
114
|
-
? new Date(String(c.updatedAt)).toISOString().slice(0, 19).replace('T', ' ')
|
|
115
|
-
: '-',
|
|
113
|
+
updated: (0, format_1.localTime)(c.updatedAt),
|
|
116
114
|
}));
|
|
117
115
|
(0, format_1.renderTable)(rows, [
|
|
118
116
|
{ key: 'id', header: 'ID' },
|
|
@@ -5,6 +5,7 @@ const core_1 = require("@oclif/core");
|
|
|
5
5
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
6
|
const base_command_1 = require("../../base-command");
|
|
7
7
|
const web_url_1 = require("../../lib/web-url");
|
|
8
|
+
const format_1 = require("../../lib/format");
|
|
8
9
|
class ConvosShow extends base_command_1.BaseCommand {
|
|
9
10
|
static description = 'Show details for a single conversation';
|
|
10
11
|
static examples = [
|
|
@@ -38,10 +39,10 @@ class ConvosShow extends base_command_1.BaseCommand {
|
|
|
38
39
|
this.log(`Model: ${c.model ?? '-'}`);
|
|
39
40
|
this.log(`User: ${c.user ?? '-'}`);
|
|
40
41
|
if (c.createdAt) {
|
|
41
|
-
this.log(`Created: ${
|
|
42
|
+
this.log(`Created: ${(0, format_1.localTime)(c.createdAt)}`);
|
|
42
43
|
}
|
|
43
44
|
if (c.updatedAt) {
|
|
44
|
-
this.log(`Updated: ${
|
|
45
|
+
this.log(`Updated: ${(0, format_1.localTime)(c.updatedAt)}`);
|
|
45
46
|
}
|
|
46
47
|
if (c.agent_id || c.agentId) {
|
|
47
48
|
this.log(`Agent: ${typeof c.agentName === 'string' && c.agentName ? `${c.agentName} (${c.agent_id ?? c.agentId})` : (c.agent_id ?? c.agentId)}`);
|
|
@@ -102,17 +103,9 @@ function formatHandoverParty(entry, baseURL) {
|
|
|
102
103
|
if (entry.by)
|
|
103
104
|
lines.push(` By: ${entry.by}`);
|
|
104
105
|
if (entry.at)
|
|
105
|
-
lines.push(` At: ${
|
|
106
|
+
lines.push(` At: ${(0, format_1.localTime)(entry.at)}`);
|
|
106
107
|
// The reason last, because it is the sentence rather than a field.
|
|
107
108
|
if (entry.reason)
|
|
108
109
|
lines.push(` Reason: ${entry.reason}`);
|
|
109
110
|
return lines;
|
|
110
111
|
}
|
|
111
|
-
function formatTime(value) {
|
|
112
|
-
try {
|
|
113
|
-
return new Date(String(value)).toISOString().slice(0, 19).replace('T', ' ');
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
return String(value);
|
|
117
|
-
}
|
|
118
|
-
}
|