@arshdelight/practi 0.9.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.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +81 -0
  3. package/dist/client.js +84 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/cmd/blob.js +97 -0
  6. package/dist/cmd/blob.js.map +1 -0
  7. package/dist/cmd/clone.js +76 -0
  8. package/dist/cmd/clone.js.map +1 -0
  9. package/dist/cmd/comment.js +210 -0
  10. package/dist/cmd/comment.js.map +1 -0
  11. package/dist/cmd/config.js +30 -0
  12. package/dist/cmd/config.js.map +1 -0
  13. package/dist/cmd/edit.js +122 -0
  14. package/dist/cmd/edit.js.map +1 -0
  15. package/dist/cmd/init.js +11 -0
  16. package/dist/cmd/init.js.map +1 -0
  17. package/dist/cmd/lifecycle.js +69 -0
  18. package/dist/cmd/lifecycle.js.map +1 -0
  19. package/dist/cmd/login.js +125 -0
  20. package/dist/cmd/login.js.map +1 -0
  21. package/dist/cmd/ls.js +59 -0
  22. package/dist/cmd/ls.js.map +1 -0
  23. package/dist/cmd/new.js +57 -0
  24. package/dist/cmd/new.js.map +1 -0
  25. package/dist/cmd/pull.js +94 -0
  26. package/dist/cmd/pull.js.map +1 -0
  27. package/dist/cmd/push.js +84 -0
  28. package/dist/cmd/push.js.map +1 -0
  29. package/dist/cmd/remote.js +48 -0
  30. package/dist/cmd/remote.js.map +1 -0
  31. package/dist/cmd/search.js +137 -0
  32. package/dist/cmd/search.js.map +1 -0
  33. package/dist/cmd/show.js +61 -0
  34. package/dist/cmd/show.js.map +1 -0
  35. package/dist/cmd/skill.js +198 -0
  36. package/dist/cmd/skill.js.map +1 -0
  37. package/dist/cmd/spec.js +10 -0
  38. package/dist/cmd/spec.js.map +1 -0
  39. package/dist/cmd/update.js +60 -0
  40. package/dist/cmd/update.js.map +1 -0
  41. package/dist/credentials.js +65 -0
  42. package/dist/credentials.js.map +1 -0
  43. package/dist/index.js +412 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/oauth.js +244 -0
  46. package/dist/oauth.js.map +1 -0
  47. package/dist/render.js +25 -0
  48. package/dist/render.js.map +1 -0
  49. package/dist/state.js +74 -0
  50. package/dist/state.js.map +1 -0
  51. package/dist/version.js +12 -0
  52. package/dist/version.js.map +1 -0
  53. package/dist/web.js +243 -0
  54. package/dist/web.js.map +1 -0
  55. package/dist/workspace.js +38 -0
  56. package/dist/workspace.js.map +1 -0
  57. package/package.json +48 -0
  58. package/skills/use-practi/SKILL.md +186 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cmd/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAM9D,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;IACjD,MAAM,KAAK,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC;IACP,IAAI,CAAC;QACH,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,EAAE,GAAG,IAAI,CAAC;IACZ,CAAC;IACD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC;IACnC,MAAM,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,cAAc,WAAW,gBAAgB,gBAAgB,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5E,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC,gBAAgB,KAAK,CAAC,WAAW,IAAI,YAAY,KAAK,gBAAgB,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAClK,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,eAAe,QAAQ,EAAE,CAAC,CAAC;IACvC,IAAI,EAAE,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpC,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,WAAW,CAAC,MAAM,iDAAiD,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,122 @@
1
+ import fs from 'node:fs';
2
+ import { createFromDoc, loadWorkspace, validateWorkspace, resolveNodeRef, nodeFilePath, } from '@arshdelight/pop-sdk';
3
+ import { defaultDataDir, loadState, saveState } from '../state.js';
4
+ import { openWorkspace } from '../workspace.js';
5
+ function readStdin() {
6
+ return fs.readFileSync(0, 'utf8');
7
+ }
8
+ function isRecord(v) {
9
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
10
+ }
11
+ function short(hash) {
12
+ return hash.slice('sha256:'.length, 'sha256:'.length + 10);
13
+ }
14
+ /**
15
+ * 从全部 direct 根做可达性分析(沿 children pins 走——内联与 ChildRef 同形存储,无需区分),
16
+ * 返回工作区里不可达的节点哈希。即 hub reconcileIndirectClaims 的本地对应:
17
+ * 引用计数(被几个 direct 根的后代集合包含)归 0 才清除。
18
+ */
19
+ export function collectUnreachable(ws, directRoots) {
20
+ const seen = new Set();
21
+ const stack = [...directRoots];
22
+ while (stack.length > 0) {
23
+ const h = stack.pop();
24
+ if (seen.has(h))
25
+ continue;
26
+ const node = ws.nodes.get(h);
27
+ if (!node)
28
+ continue;
29
+ seen.add(h);
30
+ if (node.type === 'practice') {
31
+ for (const c of node.children)
32
+ stack.push(c.hash);
33
+ }
34
+ }
35
+ return [...ws.nodes.keys()].filter((h) => !seen.has(h));
36
+ }
37
+ /**
38
+ * practi edit <hash>:非交互编辑一个 direct 根——新文档(file/--json/stdin)→ 新哈希换注册。
39
+ * 编辑即修订:根节点自动追加 revisions(from = 旧根哈希;spec §2.2 历史指针,允许悬空、永不校验)。
40
+ * 默认 GC 不可达节点(旧根及其独有后代;仍被其它 direct 根引用的内容保留);--keep 跳过。
41
+ * 纯本地操作:远端旧版仍在,结束时提示 push + delete 同步。
42
+ */
43
+ export function runEdit(opts) {
44
+ const ref = opts.positional[0];
45
+ let text;
46
+ if (opts.json !== undefined)
47
+ text = opts.json;
48
+ else if (opts.file)
49
+ text = fs.readFileSync(opts.file, 'utf8');
50
+ else if (opts.positional[1])
51
+ text = fs.readFileSync(opts.positional[1], 'utf8');
52
+ else if (!process.stdin.isTTY)
53
+ text = readStdin();
54
+ if (!ref || text === undefined || text.trim() === '') {
55
+ console.error("usage: practi edit <hash> <file.json> | practi edit <hash> --json '<text>' | practi edit <hash> < file.json");
56
+ console.error(' [--message <text>] [--no-revision] [--keep]');
57
+ return 1;
58
+ }
59
+ const dataDir = opts.dataDir ?? defaultDataDir();
60
+ const state = loadState(dataDir);
61
+ const ws = openWorkspace(dataDir);
62
+ const oldRoot = resolveNodeRef(ws, ref);
63
+ if (!state.direct.includes(oldRoot)) {
64
+ console.error(`error: ${oldRoot} is not one of your direct pops — edit swaps a direct root;`);
65
+ console.error(' for anything else fork it instead (practi show <hash> --doc, change, practi new)');
66
+ return 1;
67
+ }
68
+ let doc;
69
+ try {
70
+ doc = JSON.parse(text);
71
+ }
72
+ catch (e) {
73
+ console.error(`error [E_JSON]: not valid JSON — ${e.message}`);
74
+ return 1;
75
+ }
76
+ if (!isRecord(doc)) {
77
+ console.error('error [E_SCHEMA]: the document must be a JSON object (one tree, root included)');
78
+ return 1;
79
+ }
80
+ if (opts.noRevision !== true) {
81
+ const revisions = Array.isArray(doc.revisions) ? doc.revisions : [];
82
+ const already = revisions.some((r) => isRecord(r) && r.from === oldRoot);
83
+ if (!already) {
84
+ revisions.push({
85
+ when: new Date().toISOString().slice(0, 10),
86
+ what: opts.message ?? 'edited via practi edit',
87
+ from: oldRoot,
88
+ });
89
+ doc.revisions = revisions;
90
+ }
91
+ }
92
+ const { root: newRoot, count } = createFromDoc(ws, doc);
93
+ const issues = validateWorkspace(loadWorkspace(dataDir));
94
+ if (issues.length > 0) {
95
+ for (const i of issues) {
96
+ console.error(` ${i.code}: ${i.message}${i.hint ? ` (${i.hint})` : ''}`);
97
+ }
98
+ console.error(`warning: new tree stored (hash ${newRoot}) but NOT swapped in — ${issues.length} validation issue(s); old pop untouched`);
99
+ return 1;
100
+ }
101
+ if (newRoot === oldRoot) {
102
+ console.log(`unchanged: ${oldRoot} (content hashes identically, nothing to swap)`);
103
+ return 0;
104
+ }
105
+ state.direct = state.direct.filter((h) => h !== oldRoot);
106
+ if (!state.direct.includes(newRoot))
107
+ state.direct.push(newRoot);
108
+ saveState(dataDir, state);
109
+ console.log(`edited: ${oldRoot} -> ${newRoot} (${count} nodes)`);
110
+ if (opts.keep === true) {
111
+ console.log('gc: skipped (--keep) — old nodes kept on disk');
112
+ }
113
+ else {
114
+ const dead = collectUnreachable(loadWorkspace(dataDir), state.direct);
115
+ for (const h of dead)
116
+ fs.unlinkSync(nodeFilePath(dataDir, h));
117
+ console.log(`gc: removed ${dead.length} unreachable node(s)${dead.length > 0 ? ` — ${dead.map(short).join(', ')}` : ''}`);
118
+ }
119
+ console.log(`remote: the hub still holds the old version — sync with \`practi push\` then \`practi delete ${oldRoot}\``);
120
+ return 0;
121
+ }
122
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../src/cmd/edit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import path from 'node:path';
2
+ import { defaultDataDir } from '../state.js';
3
+ import { initDataDir } from '../workspace.js';
4
+ export function runInit(opts) {
5
+ const target = opts.positional[0] ? path.resolve(opts.positional[0]) : opts.dataDir ?? defaultDataDir();
6
+ const result = initDataDir(target);
7
+ console.log(result === 'exists' ? `already initialized: ${target}` : `initialized: ${target}`);
8
+ console.log(`use --data-dir <path> on other commands to target this directory`);
9
+ return 0;
10
+ }
11
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cmd/init.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAO9C,MAAM,UAAU,OAAO,CAAC,IAAc;IACpC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,IAAI,cAAc,EAAE,CAAC;IACxG,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,wBAAwB,MAAM,EAAE,CAAC,CAAC,CAAC,gBAAgB,MAAM,EAAE,CAAC,CAAC;IAC/F,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;IAChF,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,69 @@
1
+ import { defaultDataDir, loadState } from '../state.js';
2
+ import { authedFetch } from '../client.js';
3
+ /**
4
+ * practi submit [hash]:提交公开审核(PRIVATE → PENDING_REVIEW,自动机审通过后 PUBLISHED;此前已过审免二审)。
5
+ * POST /api/v1/pop/:ref/submit。不带 hash 默认提交全部 direct roots(逐个尝试,非 PRIVATE 的跳过并计失败)。
6
+ */
7
+ export async function runSubmit(opts) {
8
+ return runLifecycle(opts, 'submit', 'submitted');
9
+ }
10
+ /**
11
+ * practi unpublish [hash]:撤回待审 / 下架已公开(PENDING_REVIEW|PUBLISHED → PRIVATE,退出公开检索)。
12
+ * POST /api/v1/pop/:ref/unpublish。
13
+ */
14
+ export async function runUnpublish(opts) {
15
+ return runLifecycle(opts, 'unpublish', 'unpublished');
16
+ }
17
+ /**
18
+ * practi delete <hash>:删除自己在 remote 上的 DIRECT 认领(无认领的文档被硬删除)。
19
+ * DELETE /api/v1/pop/:ref。必须显式给 hash(不做默认全删);纯远端操作,本地工作区不动,
20
+ * 再次 push 同内容会重建(新行,PRIVATE)。
21
+ */
22
+ export async function runDelete(opts) {
23
+ if (opts.positional.length === 0) {
24
+ console.error('usage: practi delete <hash> (explicit hash required — no default)');
25
+ return 1;
26
+ }
27
+ return runLifecycle(opts, undefined, 'deleted');
28
+ }
29
+ async function runLifecycle(opts, action, // undefined → DELETE
30
+ verb) {
31
+ const dataDir = opts.dataDir ?? defaultDataDir();
32
+ const state = loadState(dataDir);
33
+ if (!state.remote) {
34
+ console.error('error: no remote configured — run `practi remote set <url>` first');
35
+ return 1;
36
+ }
37
+ const refs = action === undefined ? opts.positional : (opts.positional[0] ? [opts.positional[0]] : state.direct);
38
+ if (refs.length === 0) {
39
+ console.error(action
40
+ ? 'error: no direct pops to ' + action + ' (none registered)'
41
+ : 'usage: practi delete <hash>');
42
+ return 1;
43
+ }
44
+ let failed = 0;
45
+ for (const ref of refs) {
46
+ try {
47
+ const path = action === undefined
48
+ ? `/api/v1/pop/${encodeURIComponent(ref)}`
49
+ : `/api/v1/pop/${encodeURIComponent(ref)}/${action}`;
50
+ const res = await authedFetch(dataDir, state.remote.url, path, {
51
+ method: action === undefined ? 'DELETE' : 'POST',
52
+ });
53
+ const body = (await res.json().catch(() => ({})));
54
+ if (!res.ok) {
55
+ const detail = body.code ? `${body.code}: ${body.message ?? body.error}` : (body.message ?? body.error ?? `HTTP ${res.status}`);
56
+ console.error(`${verb} failed: ${ref} — ${detail}`);
57
+ failed++;
58
+ continue;
59
+ }
60
+ console.log(`${verb}: ${body.root_hash ?? ref} (${body.status ?? ''})`);
61
+ }
62
+ catch (e) {
63
+ console.error(`${verb} failed: ${ref} — ${e.message}`);
64
+ failed++;
65
+ }
66
+ }
67
+ return failed ? 1 : 0;
68
+ }
69
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/cmd/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,125 @@
1
+ import { defaultDataDir, loadState } from '../state.js';
2
+ import { saveCredentials, deleteCredentials, loadCredentials, isAccessTokenFresh, authPath } from '../credentials.js';
3
+ import { cliResource, discoverAs, registerClient, createLoopbackSession, generatePkcePair, buildAuthorizeUrl, openAuthorizeUrl, exchangeCode, revokeToken, LOGIN_SCOPES, } from '../oauth.js';
4
+ import { authedFetch, validAccessToken, HubUnreachableError } from '../client.js';
5
+ /**
6
+ * practi login:OAuth 2.1 Authorization Code + PKCE(loopback 回调)登录 practihub。
7
+ * 浏览器授权 → 换 access+refresh token → 凭证存 $dataDir/practi.auth.json(独立于 workspace 状态)。
8
+ * 之后命令自动用 refresh token 续期(rotation),无需再次交互。
9
+ */
10
+ export async function runLogin(opts) {
11
+ const dataDir = opts.dataDir ?? defaultDataDir();
12
+ const state = loadState(dataDir);
13
+ if (!state.remote) {
14
+ console.error('error: no remote configured — run `practi remote set <url>` first');
15
+ return 1;
16
+ }
17
+ const remote = state.remote.url;
18
+ const resource = cliResource(remote);
19
+ const existing = loadCredentials(dataDir);
20
+ if (existing) {
21
+ if (existing.resource && existing.resource !== resource) {
22
+ // 凭据由另一个 hub 签发,对当前 remote 无用——清掉直接走新登录
23
+ console.error(`note: stored credentials were issued for ${existing.resource}, not ${resource} — clearing them`);
24
+ deleteCredentials(dataDir);
25
+ }
26
+ else if (isAccessTokenFresh(existing)) {
27
+ console.error('error: already logged in — run `practi logout` first to re-authenticate');
28
+ return 1;
29
+ }
30
+ else {
31
+ // access 已过期:探一次 refresh。被拒=凭据已死,自动清除后继续;连不上=生死未知,保留并退出
32
+ try {
33
+ await validAccessToken(dataDir, remote);
34
+ console.error('error: already logged in — run `practi logout` first to re-authenticate');
35
+ return 1;
36
+ }
37
+ catch (e) {
38
+ if (e instanceof HubUnreachableError) {
39
+ console.error(`error: already logged in, but the session cannot be verified — ${e.message}`);
40
+ console.error(' run `practi logout` to force re-login');
41
+ return 1;
42
+ }
43
+ console.error(`note: stored credentials are dead — clearing them (${e.message})`);
44
+ deleteCredentials(dataDir);
45
+ }
46
+ }
47
+ }
48
+ console.log(`logging in to ${remote}`);
49
+ console.log(`resource: ${resource}`);
50
+ console.log(`scopes: ${LOGIN_SCOPES}`);
51
+ const meta = await discoverAs(remote);
52
+ const session = createLoopbackSession();
53
+ try {
54
+ const { client_id: clientId } = await registerClient(remote, session.redirectUri);
55
+ const pkce = generatePkcePair();
56
+ const url = buildAuthorizeUrl({
57
+ authorizationEndpoint: meta.authorization_endpoint,
58
+ clientId,
59
+ redirectUri: session.redirectUri,
60
+ state: session.state,
61
+ codeChallenge: pkce.challenge,
62
+ resource,
63
+ });
64
+ openAuthorizeUrl(url, opts.noOpen !== true);
65
+ const code = await session.waitForCode();
66
+ const tokens = await exchangeCode(remote, {
67
+ clientId,
68
+ redirectUri: session.redirectUri,
69
+ code,
70
+ verifier: pkce.verifier,
71
+ });
72
+ saveCredentials(dataDir, {
73
+ schema: 1,
74
+ client_id: clientId,
75
+ resource,
76
+ scope: tokens.scope || LOGIN_SCOPES,
77
+ client_name: 'practi cli',
78
+ access_token: tokens.access_token,
79
+ refresh_token: tokens.refresh_token,
80
+ expires_at: Date.now() + tokens.expires_in * 1000,
81
+ });
82
+ console.log(`logged in to ${remote} — credentials stored in ${authPath(dataDir)}`);
83
+ return 0;
84
+ }
85
+ finally {
86
+ session.close();
87
+ }
88
+ }
89
+ /** practi logout:服务端 revoke(best-effort)+ 删除本地凭证 */
90
+ export async function runLogout(opts) {
91
+ const dataDir = opts.dataDir ?? defaultDataDir();
92
+ const state = loadState(dataDir);
93
+ const creds = loadCredentials(dataDir);
94
+ if (creds && state.remote) {
95
+ await revokeToken(state.remote.url, creds.client_id, creds.refresh_token, 'refresh_token');
96
+ }
97
+ deleteCredentials(dataDir);
98
+ console.log('logged out');
99
+ return 0;
100
+ }
101
+ /** practi me:校验登录态,显示当前用户/角色/scope */
102
+ export async function runMe(opts) {
103
+ const dataDir = opts.dataDir ?? defaultDataDir();
104
+ const state = loadState(dataDir);
105
+ if (!state.remote) {
106
+ console.error('error: no remote configured — run `practi remote set <url>` first');
107
+ return 1;
108
+ }
109
+ const res = await authedFetch(dataDir, state.remote.url, '/api/auth/me');
110
+ const body = (await res.json().catch(() => ({})));
111
+ if (!res.ok) {
112
+ console.error(`error: not authenticated — ${typeof body.error === 'string' ? body.error : `HTTP ${res.status}`}`);
113
+ return 1;
114
+ }
115
+ console.log(`username: ${body.username ?? '(unknown)'}`);
116
+ console.log(`profile: ${body.profileUsername ?? '(none)'}`);
117
+ if (body.role)
118
+ console.log(`role: ${body.role}`);
119
+ const creds = loadCredentials(dataDir);
120
+ if (creds?.scope)
121
+ console.log(`scopes: ${creds.scope}`);
122
+ console.log(`remote: ${state.remote.url}`);
123
+ return 0;
124
+ }
125
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cmd/login.ts"],"names":[],"mappings":"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"}
package/dist/cmd/ls.js ADDED
@@ -0,0 +1,59 @@
1
+ import { aggregateView } from '@arshdelight/pop-sdk';
2
+ import { defaultDataDir, loadState } from '../state.js';
3
+ import { openWorkspace } from '../workspace.js';
4
+ import { renderSteps, shortHash, viewHeader } from '../render.js';
5
+ export function runLs(opts) {
6
+ const dataDir = opts.dataDir ?? defaultDataDir();
7
+ const ws = openWorkspace(dataDir);
8
+ const state = loadState(dataDir);
9
+ // direct = registered roots (own uploads); indirect = every other node in the workspace
10
+ const direct = state.direct.filter(h => ws.nodes.has(h));
11
+ const directSet = new Set(state.direct);
12
+ const indirect = [...ws.nodes.keys()].filter(h => !directSet.has(h));
13
+ if (opts.json) {
14
+ const views = direct.map(h => aggregateView(h, ws.nodes));
15
+ const indirectNodes = indirect.map(h => {
16
+ const n = ws.nodes.get(h);
17
+ return { hash: h, name: n.name, type: n.type, ...(n.type === 'practice' ? { op: n.op } : {}) };
18
+ });
19
+ console.log(JSON.stringify({ direct: views, indirect: indirectNodes }, null, 2));
20
+ return 0;
21
+ }
22
+ if (direct.length === 0) {
23
+ console.log('(no direct pops — create one with `practi new <file.json>` or `practi new --json \'{...}\'`)');
24
+ }
25
+ for (const h of direct) {
26
+ const view = aggregateView(h, ws.nodes);
27
+ console.log(viewHeader(view));
28
+ for (const line of renderSteps(view.steps, ws.nodes))
29
+ console.log(` ${line}`);
30
+ }
31
+ if (opts.all && indirect.length > 0) {
32
+ console.log('\nINDIRECT (nodes referenced by direct pops, not registered as direct):');
33
+ // which direct root references each indirect node
34
+ const refsByNode = new Map();
35
+ for (const h of direct) {
36
+ const stepRefs = new Set(aggregateView(h, ws.nodes).steps.map(s => s.refHash));
37
+ for (const i of indirect) {
38
+ if (stepRefs.has(i)) {
39
+ const list = refsByNode.get(i) ?? [];
40
+ list.push(ws.nodes.get(h).name);
41
+ refsByNode.set(i, list);
42
+ }
43
+ }
44
+ }
45
+ for (const h of indirect) {
46
+ const n = ws.nodes.get(h);
47
+ const refs = refsByNode.get(h);
48
+ const refText = refs ? ` (referenced by: ${refs.join(', ')})` : '';
49
+ console.log(` ${shortHash(h)} ${n.name} ${n.type === 'practice' ? `[practice·${n.op}]` : '[action]'}${refText}`);
50
+ }
51
+ }
52
+ if (ws.parseIssues.length > 0) {
53
+ console.error(`\nwarning: ${ws.parseIssues.length} parse issue(s) in the workspace`);
54
+ for (const i of ws.parseIssues)
55
+ console.error(` ${i.code}: ${i.message}`);
56
+ }
57
+ return 0;
58
+ }
59
+ //# sourceMappingURL=ls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ls.js","sourceRoot":"","sources":["../../src/cmd/ls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import fs from 'node:fs';
2
+ import { createFromDoc, loadWorkspace, validateWorkspace } from '@arshdelight/pop-sdk';
3
+ import { defaultDataDir, loadState, saveState } from '../state.js';
4
+ import { openWorkspace } from '../workspace.js';
5
+ function readStdin() {
6
+ return fs.readFileSync(0, 'utf8');
7
+ }
8
+ /**
9
+ * Create a POP from a JSON document: --json '<text>', <file.json>, or stdin
10
+ * (all machine/AI friendly — no editor loop). Validates through the SDK,
11
+ * persists the content-addressed tree, and registers the root as direct.
12
+ */
13
+ export function runNew(opts) {
14
+ let text;
15
+ if (opts.json !== undefined)
16
+ text = opts.json;
17
+ else if (opts.file)
18
+ text = fs.readFileSync(opts.file, 'utf8');
19
+ else if (opts.positional[0])
20
+ text = fs.readFileSync(opts.positional[0], 'utf8');
21
+ else if (!process.stdin.isTTY)
22
+ text = readStdin();
23
+ if (text === undefined || text.trim() === '') {
24
+ console.error('usage: practi new <file.json> | practi new --json \'<json text>\' | practi new < file.json');
25
+ return 1;
26
+ }
27
+ let doc;
28
+ try {
29
+ doc = JSON.parse(text);
30
+ }
31
+ catch (e) {
32
+ console.error(`error [E_JSON]: not valid JSON — ${e.message}`);
33
+ return 1;
34
+ }
35
+ const dataDir = opts.dataDir ?? defaultDataDir();
36
+ const ws = openWorkspace(dataDir);
37
+ const { root, count } = createFromDoc(ws, doc);
38
+ const issues = validateWorkspace(loadWorkspace(dataDir));
39
+ const state = loadState(dataDir);
40
+ if (issues.length === 0 && !state.direct.includes(root)) {
41
+ state.direct.push(root);
42
+ saveState(dataDir, state);
43
+ }
44
+ console.log(`created: ${root}`);
45
+ console.log(`nodes: ${count}`);
46
+ if (issues.length > 0) {
47
+ for (const i of issues) {
48
+ console.error(` ${i.code}: ${i.message}${i.hint ? ` (${i.hint})` : ''}`);
49
+ }
50
+ console.error(`warning: stored but NOT registered as direct — ${issues.length} validation issue(s)`);
51
+ }
52
+ else {
53
+ console.log('status: valid, registered as direct');
54
+ }
55
+ return 0;
56
+ }
57
+ //# sourceMappingURL=new.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"new.js","sourceRoot":"","sources":["../../src/cmd/new.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ import { createFromDoc, parseDocument } from '@arshdelight/pop-sdk';
2
+ import { defaultDataDir, loadState, saveState } from '../state.js';
3
+ import { openWorkspace } from '../workspace.js';
4
+ import { authedFetch, fetchMine } from '../client.js';
5
+ /**
6
+ * practi pull [hash]:把 hub 上「我的」认领同步到本地(git pull 语义)。
7
+ * - 分页取 hub 我的认领表(mine),diff 出本地还没认领的,逐个拉文档体并注册为 direct。
8
+ * - 只碰「我的认领」——不涉及公共库,因此不需要归属判断(mine 里的都是自己的)。
9
+ * - 带 hash:只同步 mine 中的那一个(不在 mine 里 → 公共内容请用 `practi clone`)。
10
+ */
11
+ export async function runPull(opts) {
12
+ const dataDir = opts.dataDir ?? defaultDataDir();
13
+ const state = loadState(dataDir);
14
+ if (!state.remote) {
15
+ console.error('error: no remote configured — run `practi remote set <url>` first');
16
+ return 1;
17
+ }
18
+ const remote = state.remote.url;
19
+ // 分页取 hub 我的认领表(diff 基准)
20
+ let mine;
21
+ try {
22
+ mine = await fetchMine(dataDir, remote);
23
+ }
24
+ catch (e) {
25
+ console.error(`error: ${e.message}`);
26
+ return 1;
27
+ }
28
+ let hashes;
29
+ if (opts.positional[0]) {
30
+ const ref = opts.positional[0];
31
+ const hit = mine.find((r) => r.root_hash === ref || (ref.startsWith('sha256:') && r.root_hash.startsWith(ref)));
32
+ if (!hit) {
33
+ console.error(`error: ${ref} is not in your claims on the remote — for public content use \`practi clone\``);
34
+ return 1;
35
+ }
36
+ hashes = [hit.root_hash];
37
+ }
38
+ else {
39
+ hashes = mine.map((r) => r.root_hash);
40
+ }
41
+ const local = new Set(loadState(dataDir).direct);
42
+ const toPull = hashes.filter((h) => !local.has(h)); // 本地缺失的认领
43
+ if (toPull.length === 0) {
44
+ console.log(mine.length === 0 ? 'nothing to pull (no own documents on the remote)' : 'up to date — nothing new to pull');
45
+ return 0;
46
+ }
47
+ let failed = 0;
48
+ for (const ref of toPull) {
49
+ failed += await pullOne(dataDir, remote, ref);
50
+ }
51
+ // 提示本地认领而 hub 还没有的(用 push 同步)
52
+ const localOnly = [...local].filter((h) => !mine.some((r) => r.root_hash === h));
53
+ if (localOnly.length > 0) {
54
+ console.log(`note: ${localOnly.length} direct POP(s) exist only locally — run \`practi push\` to sync`);
55
+ }
56
+ return failed ? 1 : 0;
57
+ }
58
+ async function pullOne(dataDir, remote, ref) {
59
+ const res = await authedFetch(dataDir, remote, `/api/v1/pop/${encodeURIComponent(ref)}`);
60
+ const body = (await res.json().catch(() => ({})));
61
+ if (!res.ok) {
62
+ const detail = typeof body.message === 'string' ? body.message : typeof body.error === 'string' ? body.error : `HTTP ${res.status}`;
63
+ console.error(`pull failed: ${ref} — ${detail}`);
64
+ return 1;
65
+ }
66
+ if (body.document === undefined || body.document === null) {
67
+ console.error(`pull failed: ${ref} — response missing document`);
68
+ return 1;
69
+ }
70
+ // 入库前验算(内容寻址的客户端责任,hub 读路径不代验):纯导入(不落库)算出的
71
+ // 根哈希必须等于请求地址——不符即拒绝,防止「另一身份的文档被悄悄注册为我的认领」
72
+ let computedRoot;
73
+ try {
74
+ computedRoot = parseDocument(body.document).rootHash;
75
+ }
76
+ catch (e) {
77
+ console.error(`pull failed: ${ref} — invalid document: ${e.message}`);
78
+ return 1;
79
+ }
80
+ if (computedRoot !== ref) {
81
+ console.error(`pull failed: ${ref} — hash mismatch: content hashes to ${computedRoot}`);
82
+ return 1;
83
+ }
84
+ const ws = openWorkspace(dataDir);
85
+ const { root } = createFromDoc(ws, body.document);
86
+ const state = loadState(dataDir);
87
+ if (!state.direct.includes(root)) {
88
+ state.direct.push(root);
89
+ saveState(dataDir, state);
90
+ }
91
+ console.log(`pulled: ${root} (${body.status ?? 'PUBLISHED'}) [direct]`);
92
+ return 0;
93
+ }
94
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/cmd/pull.ts"],"names":[],"mappings":"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"}