kollab 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.js +247 -7
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -69,6 +69,8 @@ The CLI opens a browser flow against Kollab auth and stores credentials locally.
|
|
|
69
69
|
kollab logout
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
By default, login uses `https://kollab.im`. Use `kollab login --api-url <site>` or `KOLLAB_API_URL` only when you intentionally target test or alpha.
|
|
73
|
+
|
|
72
74
|
## What You Can Do
|
|
73
75
|
|
|
74
76
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,14 @@ var import_commander = require("commander");
|
|
|
32
32
|
var fs = __toESM(require("fs"));
|
|
33
33
|
var path = __toESM(require("path"));
|
|
34
34
|
var os = __toESM(require("os"));
|
|
35
|
+
|
|
36
|
+
// src/config/api-url.ts
|
|
37
|
+
var DEFAULT_KOLLAB_API_URL = "https://kollab.im";
|
|
38
|
+
function readEnvApiUrlOrDefault() {
|
|
39
|
+
return process.env.KOLLAB_API_URL || DEFAULT_KOLLAB_API_URL;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/auth/credential-store.ts
|
|
35
43
|
var KOLLAB_DIR = path.join(os.homedir(), ".kollab");
|
|
36
44
|
var CREDENTIALS_FILE = path.join(KOLLAB_DIR, "credentials.json");
|
|
37
45
|
function ensureDir() {
|
|
@@ -50,7 +58,7 @@ function loadCredentials() {
|
|
|
50
58
|
const envToken = (process.env.KOLLAB_API_TOKEN ?? process.env.KOLLAB_TOKEN ?? "").trim();
|
|
51
59
|
if (envToken) {
|
|
52
60
|
return {
|
|
53
|
-
api_url:
|
|
61
|
+
api_url: readEnvApiUrlOrDefault(),
|
|
54
62
|
token: envToken,
|
|
55
63
|
space_id: process.env.KOLLAB_SPACE_ID,
|
|
56
64
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -92,7 +100,7 @@ function resolveApiUrl() {
|
|
|
92
100
|
if (envUrl) return envUrl;
|
|
93
101
|
const creds = loadCredentials();
|
|
94
102
|
if (creds?.api_url) return creds.api_url;
|
|
95
|
-
return
|
|
103
|
+
return DEFAULT_KOLLAB_API_URL;
|
|
96
104
|
}
|
|
97
105
|
function requireCredentials() {
|
|
98
106
|
const creds = loadCredentials();
|
|
@@ -375,7 +383,7 @@ function registerAuthCommands(program2) {
|
|
|
375
383
|
program2.command("login").description("Authenticate with Kollab workspace").option("--token <token>", "Workspace token (skip browser login)").option(
|
|
376
384
|
"--api-url <url>",
|
|
377
385
|
"API base URL",
|
|
378
|
-
|
|
386
|
+
readEnvApiUrlOrDefault()
|
|
379
387
|
).action(async (opts) => {
|
|
380
388
|
if (opts.token) {
|
|
381
389
|
await loginWithToken(opts.apiUrl, opts.token);
|
|
@@ -1704,6 +1712,186 @@ function parseQuestionnaireAnswers(raw) {
|
|
|
1704
1712
|
return normalized;
|
|
1705
1713
|
}
|
|
1706
1714
|
|
|
1715
|
+
// src/commands/task-long-task.ts
|
|
1716
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
|
|
1717
|
+
function sleep3(ms) {
|
|
1718
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1719
|
+
}
|
|
1720
|
+
function resolveConversationId(optValue) {
|
|
1721
|
+
const envValue = process.env.CURRENT_CONVERSATION_ID?.trim();
|
|
1722
|
+
const result = optValue?.trim() || envValue || "";
|
|
1723
|
+
if (!result) {
|
|
1724
|
+
console.error(
|
|
1725
|
+
JSON.stringify({
|
|
1726
|
+
ok: false,
|
|
1727
|
+
error: "--conversation-id is required, or set CURRENT_CONVERSATION_ID in the environment."
|
|
1728
|
+
})
|
|
1729
|
+
);
|
|
1730
|
+
process.exit(1);
|
|
1731
|
+
}
|
|
1732
|
+
return result;
|
|
1733
|
+
}
|
|
1734
|
+
function registerTaskLongTaskCommand(task) {
|
|
1735
|
+
const longTask = task.command("long-task").description(
|
|
1736
|
+
"Generic long-task polling: list, check status, or watch a job to completion"
|
|
1737
|
+
);
|
|
1738
|
+
longTask.command("list").description(
|
|
1739
|
+
"List long-running jobs for a conversation (video, tts, asr, ...)"
|
|
1740
|
+
).option(
|
|
1741
|
+
"--conversation-id <uuid>",
|
|
1742
|
+
"Conversation ID (defaults to $CURRENT_CONVERSATION_ID)"
|
|
1743
|
+
).option("--limit <number>", "Maximum number of jobs to return", "20").option(
|
|
1744
|
+
"--kind <kind>",
|
|
1745
|
+
"Client-side filter: video | tts | asr | all (default: all)",
|
|
1746
|
+
"all"
|
|
1747
|
+
).action(
|
|
1748
|
+
async (opts) => {
|
|
1749
|
+
const creds = requireCredentials();
|
|
1750
|
+
const conversationId = resolveConversationId(opts.conversationId);
|
|
1751
|
+
const params = new URLSearchParams({
|
|
1752
|
+
conversationId,
|
|
1753
|
+
limit: opts.limit
|
|
1754
|
+
});
|
|
1755
|
+
const result = await api(
|
|
1756
|
+
creds,
|
|
1757
|
+
"GET",
|
|
1758
|
+
`/skills/tools/long-tasks/list?${params}`
|
|
1759
|
+
);
|
|
1760
|
+
if (result.ok && opts.kind !== "all") {
|
|
1761
|
+
const raw = result.data;
|
|
1762
|
+
const jobs = raw?.jobs ?? raw?.data;
|
|
1763
|
+
if (Array.isArray(jobs)) {
|
|
1764
|
+
const filtered = jobs.filter(
|
|
1765
|
+
(j) => j.kind === opts.kind
|
|
1766
|
+
);
|
|
1767
|
+
const key = raw?.jobs !== void 0 ? "jobs" : "data";
|
|
1768
|
+
result.data = { ...raw, [key]: filtered };
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
printResult(result);
|
|
1772
|
+
}
|
|
1773
|
+
);
|
|
1774
|
+
longTask.command("status").description("Get the current snapshot of a single long-running job").option(
|
|
1775
|
+
"--conversation-id <uuid>",
|
|
1776
|
+
"Conversation ID (defaults to $CURRENT_CONVERSATION_ID)"
|
|
1777
|
+
).requiredOption("--job-id <id>", "Job ID to query").action(async (opts) => {
|
|
1778
|
+
const creds = requireCredentials();
|
|
1779
|
+
const conversationId = resolveConversationId(opts.conversationId);
|
|
1780
|
+
const params = new URLSearchParams({
|
|
1781
|
+
conversationId,
|
|
1782
|
+
jobId: opts.jobId
|
|
1783
|
+
});
|
|
1784
|
+
const result = await api(
|
|
1785
|
+
creds,
|
|
1786
|
+
"GET",
|
|
1787
|
+
`/skills/tools/long-tasks/status?${params}`
|
|
1788
|
+
);
|
|
1789
|
+
printResult(result);
|
|
1790
|
+
});
|
|
1791
|
+
longTask.command("watch").description(
|
|
1792
|
+
"Poll a long-running job until it reaches a terminal state or timeout"
|
|
1793
|
+
).option(
|
|
1794
|
+
"--conversation-id <uuid>",
|
|
1795
|
+
"Conversation ID (defaults to $CURRENT_CONVERSATION_ID)"
|
|
1796
|
+
).requiredOption("--job-id <id>", "Job ID to watch").option("--timeout <seconds>", "Maximum seconds to wait", "300").option("--interval <seconds>", "Polling interval in seconds", "5").action(
|
|
1797
|
+
async (opts) => {
|
|
1798
|
+
const creds = requireCredentials();
|
|
1799
|
+
const conversationId = resolveConversationId(opts.conversationId);
|
|
1800
|
+
const timeoutMs = Number.parseFloat(opts.timeout) * 1e3;
|
|
1801
|
+
const intervalMs = Number.parseFloat(opts.interval) * 1e3;
|
|
1802
|
+
const isTTY = process.stdout.isTTY === true;
|
|
1803
|
+
let interrupted = false;
|
|
1804
|
+
process.on("SIGINT", () => {
|
|
1805
|
+
interrupted = true;
|
|
1806
|
+
console.log(
|
|
1807
|
+
JSON.stringify(
|
|
1808
|
+
{
|
|
1809
|
+
ok: true,
|
|
1810
|
+
data: {
|
|
1811
|
+
watch_result: "interrupted",
|
|
1812
|
+
job_id: opts.jobId,
|
|
1813
|
+
conversation_id: conversationId
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
null,
|
|
1817
|
+
2
|
|
1818
|
+
)
|
|
1819
|
+
);
|
|
1820
|
+
process.exit(130);
|
|
1821
|
+
});
|
|
1822
|
+
const params = new URLSearchParams({
|
|
1823
|
+
conversationId,
|
|
1824
|
+
jobId: opts.jobId
|
|
1825
|
+
});
|
|
1826
|
+
const startMs = Date.now();
|
|
1827
|
+
let lastSnapshot = null;
|
|
1828
|
+
let lastStatus;
|
|
1829
|
+
while (!interrupted) {
|
|
1830
|
+
const elapsed = Date.now() - startMs;
|
|
1831
|
+
if (elapsed >= timeoutMs) {
|
|
1832
|
+
console.log(
|
|
1833
|
+
JSON.stringify(
|
|
1834
|
+
{
|
|
1835
|
+
ok: true,
|
|
1836
|
+
data: {
|
|
1837
|
+
watch_result: "timeout",
|
|
1838
|
+
job_id: opts.jobId,
|
|
1839
|
+
conversation_id: conversationId,
|
|
1840
|
+
elapsed_seconds: Math.round(elapsed / 1e3),
|
|
1841
|
+
last_status: lastStatus,
|
|
1842
|
+
last_snapshot: lastSnapshot
|
|
1843
|
+
}
|
|
1844
|
+
},
|
|
1845
|
+
null,
|
|
1846
|
+
2
|
|
1847
|
+
)
|
|
1848
|
+
);
|
|
1849
|
+
process.exit(0);
|
|
1850
|
+
}
|
|
1851
|
+
const result = await api(
|
|
1852
|
+
creds,
|
|
1853
|
+
"GET",
|
|
1854
|
+
`/skills/tools/long-tasks/status?${params}`
|
|
1855
|
+
);
|
|
1856
|
+
if (!result.ok) {
|
|
1857
|
+
printResult(result);
|
|
1858
|
+
process.exit(1);
|
|
1859
|
+
}
|
|
1860
|
+
lastSnapshot = result.data;
|
|
1861
|
+
const jobData = result.data;
|
|
1862
|
+
lastStatus = jobData?.status ?? jobData?.data?.status ?? void 0;
|
|
1863
|
+
if (isTTY) {
|
|
1864
|
+
const progress = jobData?.progress ?? jobData?.data?.progress;
|
|
1865
|
+
process.stdout.write(
|
|
1866
|
+
`[watch] status=${lastStatus ?? "unknown"} progress=${progress ?? "-"} elapsed=${Math.round((Date.now() - startMs) / 1e3)}s
|
|
1867
|
+
`
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
if (lastStatus && TERMINAL_STATUSES.has(lastStatus)) {
|
|
1871
|
+
console.log(
|
|
1872
|
+
JSON.stringify(
|
|
1873
|
+
{
|
|
1874
|
+
ok: true,
|
|
1875
|
+
data: {
|
|
1876
|
+
watch_result: lastStatus === "succeeded" ? "succeeded" : "failed",
|
|
1877
|
+
job_id: opts.jobId,
|
|
1878
|
+
conversation_id: conversationId,
|
|
1879
|
+
elapsed_seconds: Math.round((Date.now() - startMs) / 1e3),
|
|
1880
|
+
...jobData ?? {}
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
null,
|
|
1884
|
+
2
|
|
1885
|
+
)
|
|
1886
|
+
);
|
|
1887
|
+
process.exit(lastStatus === "succeeded" ? 0 : 2);
|
|
1888
|
+
}
|
|
1889
|
+
await sleep3(intervalMs);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1707
1895
|
// src/commands/task.ts
|
|
1708
1896
|
function registerTaskCommands(program2) {
|
|
1709
1897
|
const task = program2.command("task").description("Task (conversation) management");
|
|
@@ -1728,6 +1916,7 @@ function registerTaskCommands(program2) {
|
|
|
1728
1916
|
registerTaskCreateCommand(task);
|
|
1729
1917
|
registerTaskAskCommand(task);
|
|
1730
1918
|
registerTaskAnswerQuestionCommand(task);
|
|
1919
|
+
registerTaskLongTaskCommand(task);
|
|
1731
1920
|
task.command("rename").requiredOption("--conversation-id <uuid>", "Conversation ID").requiredOption("--title <title>", "New title").action(async (opts) => {
|
|
1732
1921
|
const creds = requireCredentials();
|
|
1733
1922
|
const result = await api(
|
|
@@ -1971,16 +2160,67 @@ function registerSkillCommands(program2) {
|
|
|
1971
2160
|
);
|
|
1972
2161
|
printResult(result);
|
|
1973
2162
|
});
|
|
1974
|
-
skill.command("install").
|
|
2163
|
+
skill.command("install").description("Permanently install an official skill by ID or import a GitHub skill URL into the current space").option("--id <uuid>", "Official skill ID to install").option("--github-url <url>", "GitHub skill directory or SKILL.md URL to import and install").action(async (opts) => {
|
|
2164
|
+
if (opts.id && opts.githubUrl) {
|
|
2165
|
+
console.error(JSON.stringify({ ok: false, error: "--id and --github-url are mutually exclusive; provide exactly one" }, null, 2));
|
|
2166
|
+
process.exit(1);
|
|
2167
|
+
}
|
|
2168
|
+
if (!opts.id && !opts.githubUrl) {
|
|
2169
|
+
console.error(JSON.stringify({ ok: false, error: "Either --id or --github-url is required" }, null, 2));
|
|
2170
|
+
process.exit(1);
|
|
2171
|
+
}
|
|
1975
2172
|
const creds = requireCredentials();
|
|
1976
2173
|
const spaceId = requireSpaceId(creds);
|
|
1977
|
-
const result = await api(
|
|
2174
|
+
const result = opts.githubUrl ? await api(
|
|
2175
|
+
creds,
|
|
2176
|
+
"POST",
|
|
2177
|
+
"/skills/skills/market/install",
|
|
2178
|
+
{
|
|
2179
|
+
github_url: opts.githubUrl,
|
|
2180
|
+
space_id: spaceId
|
|
2181
|
+
}
|
|
2182
|
+
) : await api(
|
|
1978
2183
|
creds,
|
|
1979
2184
|
"POST",
|
|
1980
2185
|
`/skills/skills/${opts.id}/add-to-space?spaceId=${spaceId}`
|
|
1981
2186
|
);
|
|
1982
2187
|
printResult(result);
|
|
1983
2188
|
});
|
|
2189
|
+
skill.command("search").description("Paginated keyword search of the official skill catalog").option("-q, --query <query>", "Case-insensitive substring filter on name + description").option("--page <number>", "1-indexed page number (default: 1)", "1").option("--limit <number>", "Page size, 1\u201350 (default: 20)", "20").action(async (opts) => {
|
|
2190
|
+
const creds = requireCredentials();
|
|
2191
|
+
const spaceId = requireSpaceId(creds);
|
|
2192
|
+
const page = Math.max(1, parseInt(opts.page, 10) || 1);
|
|
2193
|
+
const limit = Math.min(50, Math.max(1, parseInt(opts.limit, 10) || 20));
|
|
2194
|
+
const params = new URLSearchParams({
|
|
2195
|
+
spaceId,
|
|
2196
|
+
page: String(page),
|
|
2197
|
+
limit: String(limit)
|
|
2198
|
+
});
|
|
2199
|
+
if (opts.query) {
|
|
2200
|
+
params.set("q", opts.query);
|
|
2201
|
+
}
|
|
2202
|
+
const result = await api(creds, "GET", `/skills/recommended/search?${params.toString()}`);
|
|
2203
|
+
printResult(result);
|
|
2204
|
+
});
|
|
2205
|
+
skill.command("try").description("Session-scoped hydration of an official skill into the current workspace (no space ACL write)").option("--id <uuid>", "Official skill ID (mutually exclusive with --name)").option("--name <name>", "Skill folder or display name (mutually exclusive with --id)").option("--expiration <seconds>", "Presigned URL TTL in seconds, 60\u201386400 (default: 3600)").action(async (opts) => {
|
|
2206
|
+
if (opts.id && opts.name) {
|
|
2207
|
+
console.error(JSON.stringify({ ok: false, error: "--id and --name are mutually exclusive; provide exactly one" }, null, 2));
|
|
2208
|
+
process.exit(1);
|
|
2209
|
+
}
|
|
2210
|
+
if (!opts.id && !opts.name) {
|
|
2211
|
+
console.error(JSON.stringify({ ok: false, error: "Either --id or --name is required" }, null, 2));
|
|
2212
|
+
process.exit(1);
|
|
2213
|
+
}
|
|
2214
|
+
const creds = requireCredentials();
|
|
2215
|
+
const body = {};
|
|
2216
|
+
if (opts.id) body.skill_id = opts.id;
|
|
2217
|
+
if (opts.name) body.skill_name = opts.name;
|
|
2218
|
+
if (opts.expiration) {
|
|
2219
|
+
body.expiration = Math.min(86400, Math.max(60, parseInt(opts.expiration, 10) || 3600));
|
|
2220
|
+
}
|
|
2221
|
+
const result = await api(creds, "POST", "/tools/skills/hydrate-from-catalog", body);
|
|
2222
|
+
printResult(result);
|
|
2223
|
+
});
|
|
1984
2224
|
}
|
|
1985
2225
|
|
|
1986
2226
|
// src/commands/artifact.ts
|
|
@@ -2887,7 +3127,7 @@ function registerImagineCommands(program2) {
|
|
|
2887
3127
|
imagine.command("search").description("Search the prompt gallery; results are pre-sorted by like_count.").option("-q, --query <text>", "Free-text search across title and prompts (1-3 keywords work best).").option("-t, --tag <tag>", "Filter by one canonical tag. Run `kollab imagine tags` to list.").option(
|
|
2888
3128
|
"-l, --lan <code>",
|
|
2889
3129
|
"Output language (en, zh-CN, zh-TW, ja, ko, fr, de, ru, pt-BR). Defaults to env locale."
|
|
2890
|
-
).option("--limit <n>", `Max items to return (1-${DEFAULT_LIMIT}, default ${DEFAULT_LIMIT}).`, String(DEFAULT_LIMIT)).option("--cursor <c>", "Pagination cursor from a previous response (advanced).").option("--top", "Print only items[0].prompt text \u2014 useful for piping into gptimg / seedance2.").action(runSearch);
|
|
3130
|
+
).option("--limit <n>", `Max items to return (1-${DEFAULT_LIMIT}, default ${DEFAULT_LIMIT}).`, String(DEFAULT_LIMIT)).option("--cursor <c>", "Pagination cursor from a previous response (advanced).").option("--top", "Print only items[0].prompt text \u2014 useful for piping into gptimg / seedance2 / klingv3.").action(runSearch);
|
|
2891
3131
|
imagine.command("tags").description("List the canonical tags currently available in the gallery.").option("-l, --lan <code>", "Output language (only affects how related metadata may surface later).").action(runTags);
|
|
2892
3132
|
}
|
|
2893
3133
|
|
|
@@ -2974,7 +3214,7 @@ function resolveCliVersion() {
|
|
|
2974
3214
|
}
|
|
2975
3215
|
} catch {
|
|
2976
3216
|
}
|
|
2977
|
-
return "0.1.
|
|
3217
|
+
return "0.1.5";
|
|
2978
3218
|
}
|
|
2979
3219
|
program.name("kollab").description("Kollab workspace CLI").version(resolveCliVersion());
|
|
2980
3220
|
registerAuthCommands(program);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kollab",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Kollab workspace CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kollab": "dist/index.js"
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"build": "tsup",
|
|
23
23
|
"dev": "tsup --watch",
|
|
24
24
|
"lint": "eslint .",
|
|
25
|
+
"test:public-defaults": "node ./scripts/check-public-defaults.mjs",
|
|
25
26
|
"release:canary": "node ./scripts/release-package.mjs --channel canary --publish",
|
|
26
27
|
"release:next": "node ./scripts/release-package.mjs --channel next --publish",
|
|
27
28
|
"release:latest": "node ./scripts/release-package.mjs --channel latest --publish"
|