@skrr-ai/cli 0.1.120 → 0.1.121
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.js +11 -2
- package/dist/commands/agent/message/context.d.ts +1 -0
- package/dist/commands/agent/message/context.js +3 -2
- package/dist/commands/search/agents.js +5 -1
- package/dist/commands/search/all.js +74 -22
- package/dist/commands/search/skills.d.ts +1 -0
- package/dist/commands/search/skills.js +12 -1
- package/dist/commands/search/spaces.d.ts +8 -1
- package/dist/commands/search/spaces.js +50 -12
- package/dist/commands/search/tasks.d.ts +13 -2
- package/dist/commands/search/tasks.js +78 -14
- package/dist/commands/search/teams.js +5 -1
- package/dist/commands/search/workspace.d.ts +19 -0
- package/dist/commands/search/workspace.js +126 -0
- package/dist/commands/skills/browse.d.ts +1 -0
- package/dist/commands/skills/browse.js +8 -1
- package/dist/commands/skills/search.d.ts +1 -0
- package/dist/commands/skills/search.js +12 -1
- package/dist/commands/spaces/list.d.ts +1 -0
- package/dist/commands/spaces/list.js +12 -3
- package/dist/commands/spaces/search.d.ts +1 -0
- package/dist/commands/spaces/search.js +9 -0
- package/dist/commands/tasks/search.d.ts +1 -0
- package/dist/commands/tasks/search.js +9 -0
- package/dist/lib/api-error-reason.d.ts +2 -0
- package/dist/lib/api-error-reason.js +14 -0
- package/dist/lib/api-fetch.js +3 -0
- package/dist/lib/search-display.d.ts +7 -0
- package/dist/lib/search-display.js +31 -0
- package/dist/lib/skill-store-pagination.d.ts +8 -0
- package/dist/lib/skill-store-pagination.js +35 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +10 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +147 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.d.ts +20 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.js +72 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.d.ts +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.js +96 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +2 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +102 -20
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.d.ts +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.js +161 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.d.ts +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.js +41 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +15 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/macos.js +20 -4
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +10 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +172 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.d.ts +20 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.js +43 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.d.ts +21 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.js +62 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +2 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +133 -25
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.d.ts +27 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.js +132 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.d.ts +26 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.js +15 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/macos.js +28 -15
- package/dist/node_modules/@skrr-ai/auth-core/package.json +31 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7627 -7574
- package/oclif.manifest.json +634 -21
- package/package.json +1 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command';
|
|
2
|
+
export { searchDisplayText } from '../../lib/search-display';
|
|
3
|
+
export declare function searchDiagnosticMessage(code: string): string;
|
|
4
|
+
/** Workspace search uses the same ACL-filtered index and result order as web. */
|
|
5
|
+
export default class SearchWorkspace extends BaseCommand {
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static args: {
|
|
9
|
+
query: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
10
|
+
};
|
|
11
|
+
static flags: {
|
|
12
|
+
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
type: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
cursor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
|
+
};
|
|
18
|
+
run(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchDisplayText = void 0;
|
|
4
|
+
exports.searchDiagnosticMessage = searchDiagnosticMessage;
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
7
|
+
const base_command_1 = require("../../base-command");
|
|
8
|
+
const format_1 = require("../../lib/format");
|
|
9
|
+
const search_query_1 = require("../../lib/search-query");
|
|
10
|
+
const web_url_1 = require("../../lib/web-url");
|
|
11
|
+
const search_display_1 = require("../../lib/search-display");
|
|
12
|
+
var search_display_2 = require("../../lib/search-display");
|
|
13
|
+
Object.defineProperty(exports, "searchDisplayText", { enumerable: true, get: function () { return search_display_2.searchDisplayText; } });
|
|
14
|
+
function isSearchResponse(value) {
|
|
15
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
16
|
+
return false;
|
|
17
|
+
const response = value;
|
|
18
|
+
if (!Array.isArray(response.results) || (response.nextCursor !== null && (typeof response.nextCursor !== 'string' || !(0, search_display_1.isPrintableSearchCursor)(response.nextCursor))) ||
|
|
19
|
+
(response.diagnostics !== undefined && (!Array.isArray(response.diagnostics) || !response.diagnostics.every((item) => typeof item === 'string'))))
|
|
20
|
+
return false;
|
|
21
|
+
return response.results.every((item) => item && typeof item === 'object' && !Array.isArray(item) &&
|
|
22
|
+
['id', 'type', 'entityId', 'title', 'href'].every((field) => typeof item[field] === 'string') &&
|
|
23
|
+
item.href.startsWith('/') && !item.href.startsWith('//') && !item.href.includes('\\') &&
|
|
24
|
+
!/[\u0000-\u001f\u007f]/.test(item.href) &&
|
|
25
|
+
['identifier', 'excerpt'].every((field) => item[field] === undefined || typeof item[field] === 'string'));
|
|
26
|
+
}
|
|
27
|
+
function searchDiagnosticMessage(code) {
|
|
28
|
+
switch (code) {
|
|
29
|
+
case 'global_search_index_stale':
|
|
30
|
+
return 'The search index is catching up. Recent changes may be missing; retry shortly.';
|
|
31
|
+
case 'global_search_acl_unavailable':
|
|
32
|
+
return 'Access checks were unavailable. Results may be incomplete; retry shortly.';
|
|
33
|
+
case 'global_search_source_unavailable':
|
|
34
|
+
return 'Some current resources could not be checked. Results may be incomplete; retry shortly.';
|
|
35
|
+
case 'global_search_candidate_window_may_be_limited':
|
|
36
|
+
return 'This broad search may exceed the index result window. Narrow the query or select resource types.';
|
|
37
|
+
case 'global_search_candidate_window_unknown':
|
|
38
|
+
return 'The index result window could not be checked. Results may be incomplete; retry shortly.';
|
|
39
|
+
case 'global_search_invalid_candidate':
|
|
40
|
+
return 'The search index returned invalid candidates. Results may be incomplete; retry shortly.';
|
|
41
|
+
default:
|
|
42
|
+
return `Search may be incomplete (${(0, search_display_1.searchDisplayText)(code)}). Retry or inspect --json diagnostics.`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Workspace search uses the same ACL-filtered index and result order as web. */
|
|
46
|
+
class SearchWorkspace extends base_command_1.BaseCommand {
|
|
47
|
+
static description = 'Search supported resources in your workspace';
|
|
48
|
+
static examples = [
|
|
49
|
+
'<%= config.bin %> search workspace "deployment incident"',
|
|
50
|
+
'<%= config.bin %> search workspace pricing --type task --type wiki --json',
|
|
51
|
+
];
|
|
52
|
+
static args = { query: core_1.Args.string({ description: 'Search term', ignoreStdin: true }) };
|
|
53
|
+
static flags = {
|
|
54
|
+
query: core_1.Flags.string({ char: 'q', description: 'Search term' }),
|
|
55
|
+
type: core_1.Flags.string({ multiple: true, description: 'Limit to resource types (repeatable; for example task, space, wiki, message)' }),
|
|
56
|
+
limit: core_1.Flags.integer({ default: 25, min: 1, max: 50, description: 'Results per page' }),
|
|
57
|
+
cursor: core_1.Flags.string({ description: 'Continue from a previous page' }),
|
|
58
|
+
json: core_1.Flags.boolean({ description: 'Output structured results and diagnostics' }),
|
|
59
|
+
};
|
|
60
|
+
async run() {
|
|
61
|
+
this.requireAuth();
|
|
62
|
+
const { args, flags } = await this.parse(SearchWorkspace);
|
|
63
|
+
const query = (0, search_query_1.resolveSearchQuery)(this, args.query, flags.query, 'search workspace');
|
|
64
|
+
// Session-only tokens cannot authenticate to the JWT route. Never adopt an
|
|
65
|
+
// ambient human credential to turn this into broader authority.
|
|
66
|
+
if (this.resolvedCredential.token?.startsWith('agst_')) {
|
|
67
|
+
this.failWithCliError({
|
|
68
|
+
message: 'Workspace search is not available with a session-scoped credential. Ask the workspace owner to enable the delegated CLI surface for a new Agent session, or search in a signed-in human session. Do not substitute a personal login inside the Agent.',
|
|
69
|
+
code: 'GLOBAL_SEARCH_SESSION_UNSUPPORTED', exit: 2, retryable: false,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
let response;
|
|
73
|
+
try {
|
|
74
|
+
response = await data_provider_1.dataService.searchGlobal({ query, types: flags.type, limit: flags.limit, cursor: flags.cursor });
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const code = (0, base_command_1.apiRefusal)(err).code;
|
|
78
|
+
if (code === 'SEARCH_WORKSPACE_REQUIRED' || code === 'SEARCH_WORKSPACE_FORBIDDEN') {
|
|
79
|
+
this.failWithCliError({
|
|
80
|
+
message: `Choose a workspace you belong to: run \`${this.config.bin} workspaces list\`, then \`${this.config.bin} workspaces use <workspace>\`, or pass --workspace <workspace-id>.`,
|
|
81
|
+
code, status: 403, exit: 3, retryable: false,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (code === 'INVALID_SEARCH_TYPE') {
|
|
85
|
+
const allowed = (0, base_command_1.apiRefusal)(err).body?.supportedTypes;
|
|
86
|
+
const types = Array.isArray(allowed)
|
|
87
|
+
? allowed.filter((type) => typeof type === 'string').map(search_display_1.searchDisplayText).filter(Boolean)
|
|
88
|
+
: [];
|
|
89
|
+
this.failWithCliError({
|
|
90
|
+
message: types.length
|
|
91
|
+
? `Unknown search type. Choose from: ${types.join(', ')}.`
|
|
92
|
+
: 'Unknown search type. Run the command without --type to search all supported types.',
|
|
93
|
+
code, status: 400, exit: 2, retryable: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
this.handleApiError(err);
|
|
97
|
+
}
|
|
98
|
+
if (!isSearchResponse(response) || (flags.cursor && response.nextCursor === flags.cursor)) {
|
|
99
|
+
this.failWithCliError({ message: 'The server returned an invalid workspace search response. Retry or check server compatibility.',
|
|
100
|
+
code: 'GLOBAL_SEARCH_INVALID_RESPONSE', exit: 1, retryable: true });
|
|
101
|
+
}
|
|
102
|
+
const results = response.results.map((item) => ({
|
|
103
|
+
...item,
|
|
104
|
+
url: `${(0, web_url_1.webOrigin)(this.cliConfig.baseURL)}${item.href}`,
|
|
105
|
+
}));
|
|
106
|
+
if (flags.json) {
|
|
107
|
+
this.log(JSON.stringify({ ...response, results }, null, 2));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (const diagnostic of new Set(response.diagnostics || []))
|
|
111
|
+
this.warn(searchDiagnosticMessage(diagnostic));
|
|
112
|
+
if (results.length) {
|
|
113
|
+
(0, format_1.renderTable)(results.map((item) => ({ type: (0, search_display_1.searchDisplayText)(item.type), id: (0, search_display_1.searchDisplayText)(item.identifier || item.entityId), title: (0, search_display_1.searchDisplayText)(item.title), excerpt: (0, search_display_1.searchDisplayText)(item.excerpt || ''), url: (0, search_display_1.searchDisplayText)(item.url || '-') })), [
|
|
114
|
+
{ key: 'type', header: 'TYPE' }, { key: 'id', header: 'ID' },
|
|
115
|
+
{ key: 'title', header: 'TITLE', maxWidth: 45 }, { key: 'excerpt', header: 'EXCERPT', maxWidth: 60 },
|
|
116
|
+
{ key: 'url', header: 'URL', verbatim: true },
|
|
117
|
+
], (line) => this.log(line));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.log('No results in this page of supported workspace resources.');
|
|
121
|
+
}
|
|
122
|
+
if (response.nextCursor)
|
|
123
|
+
this.log(`Next page: --cursor ${(0, search_display_1.shellSearchCursor)(response.nextCursor)}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.default = SearchWorkspace;
|
|
@@ -16,6 +16,7 @@ export default class SkillsBrowse extends BaseCommand {
|
|
|
16
16
|
category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
sort: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
cursor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
20
|
};
|
|
20
21
|
run(): Promise<void>;
|
|
21
22
|
}
|
|
@@ -7,6 +7,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
7
7
|
const base_command_1 = require("../../base-command");
|
|
8
8
|
const format_1 = require("../../lib/format");
|
|
9
9
|
const usd_micros_1 = require("../../lib/usd-micros");
|
|
10
|
+
const skill_store_pagination_1 = require("../../lib/skill-store-pagination");
|
|
10
11
|
function skillBrowseDescription(skill) {
|
|
11
12
|
const description = String(skill.description ?? '').trim();
|
|
12
13
|
if (description && description !== '>' && description !== '|')
|
|
@@ -43,6 +44,7 @@ class SkillsBrowse extends base_command_1.BaseCommand {
|
|
|
43
44
|
options: ['newest', 'popular', 'rating'],
|
|
44
45
|
}),
|
|
45
46
|
limit: core_1.Flags.integer({ description: 'Maximum number of results', default: 20 }),
|
|
47
|
+
cursor: core_1.Flags.string({ description: 'Continue from a previous page' }),
|
|
46
48
|
};
|
|
47
49
|
async run() {
|
|
48
50
|
this.requireAuth();
|
|
@@ -59,6 +61,7 @@ class SkillsBrowse extends base_command_1.BaseCommand {
|
|
|
59
61
|
category: flags.category,
|
|
60
62
|
sort: flags.sort ? sortMap[flags.sort] : undefined,
|
|
61
63
|
limit: flags.limit,
|
|
64
|
+
cursor: flags.cursor,
|
|
62
65
|
});
|
|
63
66
|
}
|
|
64
67
|
catch (err) {
|
|
@@ -71,7 +74,10 @@ class SkillsBrowse extends base_command_1.BaseCommand {
|
|
|
71
74
|
const r = response;
|
|
72
75
|
const items = Array.isArray(r) ? r : (r?.data ?? []);
|
|
73
76
|
if (items.length === 0) {
|
|
74
|
-
this.log(
|
|
77
|
+
this.log((0, skill_store_pagination_1.skillStorePageState)(response, flags.cursor).hasMore
|
|
78
|
+
? 'No skills on this page. More candidates remain.'
|
|
79
|
+
: 'No skills found.');
|
|
80
|
+
(0, skill_store_pagination_1.reportSkillStorePage)(response, flags.cursor, (message) => this.log(message), (message) => this.warn(message));
|
|
75
81
|
return;
|
|
76
82
|
}
|
|
77
83
|
const rows = items.map(skillBrowseRow);
|
|
@@ -83,6 +89,7 @@ class SkillsBrowse extends base_command_1.BaseCommand {
|
|
|
83
89
|
{ key: 'installs', header: 'INSTALLS' },
|
|
84
90
|
{ key: 'description', header: 'DESCRIPTION', maxWidth: 60 },
|
|
85
91
|
], (line) => this.log(line));
|
|
92
|
+
(0, skill_store_pagination_1.reportSkillStorePage)(response, flags.cursor, (message) => this.log(message), (message) => this.warn(message));
|
|
86
93
|
}
|
|
87
94
|
}
|
|
88
95
|
exports.default = SkillsBrowse;
|
|
@@ -17,6 +17,7 @@ export default class SkillsSearch extends BaseCommand {
|
|
|
17
17
|
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
18
|
category: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
19
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
cursor: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
21
|
};
|
|
21
22
|
run(): Promise<void>;
|
|
22
23
|
}
|
|
@@ -6,6 +6,7 @@ const base_command_1 = require("../../base-command");
|
|
|
6
6
|
const format_1 = require("../../lib/format");
|
|
7
7
|
const search_query_1 = require("../../lib/search-query");
|
|
8
8
|
const usd_micros_1 = require("../../lib/usd-micros");
|
|
9
|
+
const skill_store_pagination_1 = require("../../lib/skill-store-pagination");
|
|
9
10
|
class SkillsSearch extends base_command_1.BaseCommand {
|
|
10
11
|
static description = 'Search skills in the skrr store by keyword (same as `search skills`)';
|
|
11
12
|
static examples = [
|
|
@@ -37,6 +38,7 @@ class SkillsSearch extends base_command_1.BaseCommand {
|
|
|
37
38
|
query: core_1.Flags.string({ char: 'q', description: 'Search query' }),
|
|
38
39
|
category: core_1.Flags.string({ description: 'Filter by category' }),
|
|
39
40
|
limit: core_1.Flags.integer({ description: 'Maximum number of results', default: 20 }),
|
|
41
|
+
cursor: core_1.Flags.string({ description: 'Continue from a previous page' }),
|
|
40
42
|
};
|
|
41
43
|
async run() {
|
|
42
44
|
this.requireAuth();
|
|
@@ -48,6 +50,7 @@ class SkillsSearch extends base_command_1.BaseCommand {
|
|
|
48
50
|
q: query,
|
|
49
51
|
category: flags.category,
|
|
50
52
|
limit: flags.limit,
|
|
53
|
+
cursor: flags.cursor,
|
|
51
54
|
});
|
|
52
55
|
}
|
|
53
56
|
catch (err) {
|
|
@@ -59,8 +62,15 @@ class SkillsSearch extends base_command_1.BaseCommand {
|
|
|
59
62
|
}
|
|
60
63
|
const r = response;
|
|
61
64
|
const items = Array.isArray(r) ? r : (r?.data ?? []);
|
|
65
|
+
const incomplete = (0, skill_store_pagination_1.skillStoreSearchIncomplete)(response);
|
|
66
|
+
if (incomplete)
|
|
67
|
+
this.warn('Skill store search may be incomplete. Try again or narrow your query.');
|
|
62
68
|
if (items.length === 0) {
|
|
63
|
-
this.log(
|
|
69
|
+
this.log((0, skill_store_pagination_1.skillStorePageState)(response, flags.cursor).hasMore
|
|
70
|
+
? 'No skills matched on this page. More candidates remain.'
|
|
71
|
+
: incomplete ? 'No skills confirmed in the available search results.'
|
|
72
|
+
: `No skills found matching '${query}'.`);
|
|
73
|
+
(0, skill_store_pagination_1.reportSkillStorePage)(response, flags.cursor, (message) => this.log(message), (message) => this.warn(message));
|
|
64
74
|
return;
|
|
65
75
|
}
|
|
66
76
|
const rows = items.map((s) => ({
|
|
@@ -79,6 +89,7 @@ class SkillsSearch extends base_command_1.BaseCommand {
|
|
|
79
89
|
{ key: 'installs', header: 'INSTALLS' },
|
|
80
90
|
{ key: 'description', header: 'DESCRIPTION', maxWidth: 60 },
|
|
81
91
|
], (line) => this.log(line));
|
|
92
|
+
(0, skill_store_pagination_1.reportSkillStorePage)(response, flags.cursor, (message) => this.log(message), (message) => this.warn(message));
|
|
82
93
|
}
|
|
83
94
|
}
|
|
84
95
|
exports.default = SkillsSearch;
|
|
@@ -25,6 +25,7 @@ export default class SpacesList extends BaseCommand {
|
|
|
25
25
|
status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
26
|
label: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
27
27
|
'include-archived': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
|
+
'archived-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
29
|
q: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
29
30
|
match: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
30
31
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -15,7 +15,7 @@ function noSpacesMatchedMessage(q, bin = 'skrr', mode = 'fuzzy') {
|
|
|
15
15
|
: mode === 'phrase'
|
|
16
16
|
? 'Phrase matches use a literal case-insensitive substring; word order matters.'
|
|
17
17
|
: 'Fuzzy discovery can use tokenized, typo-tolerant search or a substring fallback.';
|
|
18
|
-
return (`No spaces matched ${JSON.stringify(q)}.\n` +
|
|
18
|
+
return (`No spaces matched ${JSON.stringify(q)} on this page.\n` +
|
|
19
19
|
` → ${explanation} Fields: title, description and identifier.\n` +
|
|
20
20
|
` → retry with one distinctive word, or list them all with \`${bin} spaces list\`.`);
|
|
21
21
|
}
|
|
@@ -45,6 +45,7 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
45
45
|
'<%= config.bin %> spaces list --q "always on" --match phrase',
|
|
46
46
|
'<%= config.bin %> spaces list --q "My workspace" --match exact --json',
|
|
47
47
|
'<%= config.bin %> spaces list --include-archived --json',
|
|
48
|
+
'<%= config.bin %> spaces list --archived-only --json',
|
|
48
49
|
'<%= config.bin %> spaces list --compact --json',
|
|
49
50
|
'<%= config.bin %> spaces list --fields id,identifier,title --ndjson',
|
|
50
51
|
];
|
|
@@ -62,7 +63,8 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
62
63
|
description: 'Filter by label ID (repeatable). Multiple labels AND together — a space must carry all of them.',
|
|
63
64
|
multiple: true,
|
|
64
65
|
}),
|
|
65
|
-
'include-archived': core_1.Flags.boolean({ description: 'Include archived spaces' }),
|
|
66
|
+
'include-archived': core_1.Flags.boolean({ description: 'Include archived spaces', exclusive: ['archived-only'] }),
|
|
67
|
+
'archived-only': core_1.Flags.boolean({ description: 'List only archived spaces', exclusive: ['include-archived'] }),
|
|
66
68
|
q: core_1.Flags.string({
|
|
67
69
|
description: 'Search title, description, or identifier. Default fuzzy discovery may tokenize and tolerate typos; ' +
|
|
68
70
|
'use --match phrase for a literal substring or --match exact for whole-field equality. Maximum 200 characters.',
|
|
@@ -86,6 +88,7 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
86
88
|
...(flags.label?.length ? { labelIds: flags.label } : {}),
|
|
87
89
|
...(flags.q ? { q: flags.q, ...(flags.match ? { match: flags.match } : {}) } : {}),
|
|
88
90
|
includeArchived: flags['include-archived'],
|
|
91
|
+
archivedOnly: flags['archived-only'],
|
|
89
92
|
limit: flags.limit,
|
|
90
93
|
cursor: flags.cursor,
|
|
91
94
|
});
|
|
@@ -107,6 +110,8 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
107
110
|
}
|
|
108
111
|
const r = response;
|
|
109
112
|
const items = r?.data ?? [];
|
|
113
|
+
if (flags.q && (r?.degraded || r?.warnings?.length))
|
|
114
|
+
this.warn('Search may be incomplete. Inspect --json diagnostics.');
|
|
110
115
|
if (r?.search) {
|
|
111
116
|
this.log(`Search: ${r.search.mode}; ${r.search.semantics}; backend=${r.search.backend}.`);
|
|
112
117
|
if (r.search.candidates_may_be_truncated) {
|
|
@@ -116,7 +121,11 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
116
121
|
if (items.length === 0) {
|
|
117
122
|
this.log(flags.q
|
|
118
123
|
? noSpacesMatchedMessage(flags.q, this.config.bin, r?.search?.mode ?? 'fuzzy')
|
|
119
|
-
:
|
|
124
|
+
: flags['archived-only'] || flags.status || flags.label?.length || r?.next_cursor
|
|
125
|
+
? 'No spaces on this page under the current filters.'
|
|
126
|
+
: noSpacesEmptyMessage(this.config.bin));
|
|
127
|
+
if (r?.next_cursor)
|
|
128
|
+
this.log(`Next page: --cursor ${r.next_cursor}`);
|
|
120
129
|
return;
|
|
121
130
|
}
|
|
122
131
|
const rows = items.map((s) => ({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../search/spaces';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
// Both spellings share one implementation and result contract.
|
|
8
|
+
var spaces_1 = require("../search/spaces");
|
|
9
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(spaces_1).default; } });
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../search/tasks';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
// Both spellings share one implementation and result contract.
|
|
8
|
+
var tasks_1 = require("../search/tasks");
|
|
9
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(tasks_1).default; } });
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reasonOnlyCode = reasonOnlyCode;
|
|
4
|
+
/** The reason-only refusal used by session-scoped API routes. */
|
|
5
|
+
function reasonOnlyCode(body) {
|
|
6
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
7
|
+
return undefined;
|
|
8
|
+
const value = body;
|
|
9
|
+
if (value.ok !== false || typeof value.reason !== 'string')
|
|
10
|
+
return undefined;
|
|
11
|
+
const reason = value.reason.trim();
|
|
12
|
+
return reason.length <= 160 && /^[A-Za-z][A-Za-z0-9]*(?:[_-][A-Za-z0-9]+)+$/.test(reason)
|
|
13
|
+
? reason : undefined;
|
|
14
|
+
}
|
package/dist/lib/api-fetch.js
CHANGED
|
@@ -31,6 +31,7 @@ exports.hasHumanSession = hasHumanSession;
|
|
|
31
31
|
* switch to `dataService.*` without touching the wire contract.
|
|
32
32
|
*/
|
|
33
33
|
const config_1 = require("./config");
|
|
34
|
+
const api_error_reason_1 = require("./api-error-reason");
|
|
34
35
|
// ADD-CLI8 — `readFromBackend` is only used inside `hasHumanSession()`
|
|
35
36
|
// (see below), NOT by `apiFetch`. Kept as a module-scope import
|
|
36
37
|
// rather than inlined into `hasHumanSession` because (a) ES modules
|
|
@@ -282,6 +283,8 @@ async function apiFetch(pathOrUrl, opts = {}) {
|
|
|
282
283
|
const parsed = JSON.parse(text);
|
|
283
284
|
if (typeof parsed.code === 'string')
|
|
284
285
|
code = parsed.code;
|
|
286
|
+
else
|
|
287
|
+
code = (0, api_error_reason_1.reasonOnlyCode)(parsed);
|
|
285
288
|
}
|
|
286
289
|
catch {
|
|
287
290
|
/* non-JSON body */
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Keep untrusted search content from controlling or reordering a terminal. */
|
|
2
|
+
export declare function searchDisplayText(value: string): string;
|
|
3
|
+
/** Cursor values stay opaque, but must be safe to print and copy into a shell. */
|
|
4
|
+
export declare function isPrintableSearchCursor(value: string): boolean;
|
|
5
|
+
export declare function shellSearchCursor(value: string): string;
|
|
6
|
+
/** A missing or malformed list is an error, not proof of zero matches. */
|
|
7
|
+
export declare function isSearchListPage(value: unknown): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchDisplayText = searchDisplayText;
|
|
4
|
+
exports.isPrintableSearchCursor = isPrintableSearchCursor;
|
|
5
|
+
exports.shellSearchCursor = shellSearchCursor;
|
|
6
|
+
exports.isSearchListPage = isSearchListPage;
|
|
7
|
+
const node_util_1 = require("node:util");
|
|
8
|
+
/** Keep untrusted search content from controlling or reordering a terminal. */
|
|
9
|
+
function searchDisplayText(value) {
|
|
10
|
+
return (0, node_util_1.stripVTControlCharacters)(value)
|
|
11
|
+
.replace(/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/g, ' ')
|
|
12
|
+
.replace(/\s+/g, ' ')
|
|
13
|
+
.trim();
|
|
14
|
+
}
|
|
15
|
+
/** Cursor values stay opaque, but must be safe to print and copy into a shell. */
|
|
16
|
+
function isPrintableSearchCursor(value) {
|
|
17
|
+
return value.length > 0 && !/[\u0000-\u001f\u007f-\u009f\u202a-\u202e\u2066-\u2069]/.test(value);
|
|
18
|
+
}
|
|
19
|
+
function shellSearchCursor(value) {
|
|
20
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
21
|
+
}
|
|
22
|
+
/** A missing or malformed list is an error, not proof of zero matches. */
|
|
23
|
+
function isSearchListPage(value) {
|
|
24
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
25
|
+
return false;
|
|
26
|
+
const page = value;
|
|
27
|
+
return Array.isArray(page.data) && page.data.every((row) => row && typeof row === 'object' && !Array.isArray(row)) &&
|
|
28
|
+
(page.has_more === undefined || typeof page.has_more === 'boolean') &&
|
|
29
|
+
(page.next_cursor === undefined || page.next_cursor === null ||
|
|
30
|
+
(typeof page.next_cursor === 'string' && isPrintableSearchCursor(page.next_cursor)));
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The Store can return an empty page while more candidates remain to inspect. */
|
|
2
|
+
export declare function skillStoreSearchIncomplete(response: unknown): boolean;
|
|
3
|
+
export declare function skillStorePageState(response: unknown, cursor?: string): {
|
|
4
|
+
hasMore: boolean;
|
|
5
|
+
nextCursor?: string;
|
|
6
|
+
invalidCursor: boolean;
|
|
7
|
+
};
|
|
8
|
+
export declare function reportSkillStorePage(response: unknown, cursor: string | undefined, log: (message: string) => void, warn: (message: string) => void): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.skillStoreSearchIncomplete = skillStoreSearchIncomplete;
|
|
4
|
+
exports.skillStorePageState = skillStorePageState;
|
|
5
|
+
exports.reportSkillStorePage = reportSkillStorePage;
|
|
6
|
+
/** The Store can return an empty page while more candidates remain to inspect. */
|
|
7
|
+
function skillStoreSearchIncomplete(response) {
|
|
8
|
+
if (!response || Array.isArray(response) || typeof response !== 'object')
|
|
9
|
+
return false;
|
|
10
|
+
const page = response;
|
|
11
|
+
return page.partial === true || page.semanticUnavailable === true;
|
|
12
|
+
}
|
|
13
|
+
function skillStorePageState(response, cursor) {
|
|
14
|
+
if (!response || Array.isArray(response) || typeof response !== 'object') {
|
|
15
|
+
return { hasMore: false, invalidCursor: false };
|
|
16
|
+
}
|
|
17
|
+
const page = response;
|
|
18
|
+
if (page.has_more !== true)
|
|
19
|
+
return { hasMore: false, invalidCursor: false };
|
|
20
|
+
// Server cursors are opaque base64 tokens. Do not print arbitrary terminal
|
|
21
|
+
// control characters from a malformed response, or suggest a cursor loop.
|
|
22
|
+
const next = typeof page.after === 'string' && /^[A-Za-z0-9_+/=-]{1,4096}$/.test(page.after)
|
|
23
|
+
? page.after
|
|
24
|
+
: undefined;
|
|
25
|
+
if (!next || next === cursor)
|
|
26
|
+
return { hasMore: true, invalidCursor: true };
|
|
27
|
+
return { hasMore: true, nextCursor: next, invalidCursor: false };
|
|
28
|
+
}
|
|
29
|
+
function reportSkillStorePage(response, cursor, log, warn) {
|
|
30
|
+
const page = skillStorePageState(response, cursor);
|
|
31
|
+
if (page.nextCursor)
|
|
32
|
+
log(`Next page: --cursor ${page.nextCursor}`);
|
|
33
|
+
else if (page.invalidCursor)
|
|
34
|
+
warn('More results may exist, but the server did not provide a usable next cursor.');
|
|
35
|
+
}
|
|
@@ -54,6 +54,16 @@ export declare function splitKeyConvergencePolicy(): {
|
|
|
54
54
|
converge: boolean;
|
|
55
55
|
reason: 'daemon_key_pinned' | 'daemon_key_refused';
|
|
56
56
|
};
|
|
57
|
+
/** Open only the existing wrapped DEK. Recovery must never enroll a new key,
|
|
58
|
+
* consolidate profile paths or replace an unreadable envelope. A caller gets
|
|
59
|
+
* an error if the existing file or the platform's read-only unwrap is unsafe
|
|
60
|
+
* or unavailable. This intentionally does not load retired keys; credentials
|
|
61
|
+
* that require one remain unavailable rather than triggering a migration. */
|
|
62
|
+
export declare function initExistingCredEnvelope(profile: string): Promise<void>;
|
|
63
|
+
/** First enrollment for a reserved desktop background profile. Existing or
|
|
64
|
+
* ambiguous state is opened read-only or refused; it is never re-enrolled.
|
|
65
|
+
* The caller must validate the reservation before calling this function. */
|
|
66
|
+
export declare function initNewIndependentCredEnvelope(profile: string): Promise<void>;
|
|
57
67
|
/**
|
|
58
68
|
* Initialize the envelope path. Idempotent — second call when state is
|
|
59
69
|
* non-uninit returns immediately.
|