codequill 0.8.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/commands/attest.js +442 -0
- package/dist/commands/attest.js.map +1 -0
- package/dist/commands/backup.js +370 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/claim.js +104 -0
- package/dist/commands/claim.js.map +1 -0
- package/dist/commands/log.js +188 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/login.js +147 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/prove.js +244 -0
- package/dist/commands/prove.js.map +1 -0
- package/dist/commands/publish.js +243 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/pull.js +174 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/quota.js +94 -0
- package/dist/commands/quota.js.map +1 -0
- package/dist/commands/revoke.js +97 -0
- package/dist/commands/revoke.js.map +1 -0
- package/dist/commands/snapshot.js +128 -0
- package/dist/commands/snapshot.js.map +1 -0
- package/dist/commands/status.js +234 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/verifyAttestation.js +212 -0
- package/dist/commands/verifyAttestation.js.map +1 -0
- package/dist/commands/verifyProof.js +145 -0
- package/dist/commands/verifyProof.js.map +1 -0
- package/dist/commands/wait.js +36 -0
- package/dist/commands/wait.js.map +1 -0
- package/dist/commands/who.js +55 -0
- package/dist/commands/who.js.map +1 -0
- package/dist/commands/why.js +412 -0
- package/dist/commands/why.js.map +1 -0
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -0
- package/dist/launcher.js +69 -0
- package/dist/launcher.js.map +1 -0
- package/dist/services/api.js +51 -0
- package/dist/services/api.js.map +1 -0
- package/dist/services/apiClient.js +166 -0
- package/dist/services/apiClient.js.map +1 -0
- package/dist/services/authStore.js +84 -0
- package/dist/services/authStore.js.map +1 -0
- package/dist/services/config.js +19 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/confirm.js +58 -0
- package/dist/services/confirm.js.map +1 -0
- package/dist/services/crypto.js +38 -0
- package/dist/services/crypto.js.map +1 -0
- package/dist/services/errors.js +17 -0
- package/dist/services/errors.js.map +1 -0
- package/dist/services/fs.js +25 -0
- package/dist/services/fs.js.map +1 -0
- package/dist/services/git.js +121 -0
- package/dist/services/git.js.map +1 -0
- package/dist/services/manifests/attestationManifest.js +35 -0
- package/dist/services/manifests/attestationManifest.js.map +1 -0
- package/dist/services/manifests/proofManifest.js +151 -0
- package/dist/services/manifests/proofManifest.js.map +1 -0
- package/dist/services/manifests/snapshotManifest.js +214 -0
- package/dist/services/manifests/snapshotManifest.js.map +1 -0
- package/dist/services/merkle.js +92 -0
- package/dist/services/merkle.js.map +1 -0
- package/dist/services/paths.js +16 -0
- package/dist/services/paths.js.map +1 -0
- package/dist/services/snapshotIndex.js +401 -0
- package/dist/services/snapshotIndex.js.map +1 -0
- package/dist/services/txWaiter.js +84 -0
- package/dist/services/txWaiter.js.map +1 -0
- package/dist/services/ui.js +98 -0
- package/dist/services/ui.js.map +1 -0
- package/dist/services/utilities.js +45 -0
- package/dist/services/utilities.js.map +1 -0
- package/dist/services/zip.js +24 -0
- package/dist/services/zip.js.map +1 -0
- package/dist/types/api.js +2 -0
- package/dist/types/api.js.map +1 -0
- package/dist/version.js +7 -0
- package/dist/version.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { colors, shortCommit, spinner } from '../services/ui.js';
|
|
2
|
+
import { apiClient } from '../services/apiClient.js';
|
|
3
|
+
import { getBaseUrl } from '../services/config.js';
|
|
4
|
+
import { waitForTxConfirmation } from '../services/txWaiter.js';
|
|
5
|
+
import { detectGitRepo, deriveRepoName } from '../services/git.js';
|
|
6
|
+
import { SnapshotIndex } from '../services/snapshotIndex.js';
|
|
7
|
+
import { assertAuthenticated } from '../services/utilities.js';
|
|
8
|
+
import fs from 'node:fs/promises';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { gzipSync } from 'node:zlib';
|
|
11
|
+
import { fileExists } from "../services/fs.js";
|
|
12
|
+
import { confirmDangerousAction } from "../services/confirm.js";
|
|
13
|
+
export async function handlePublish(opts) {
|
|
14
|
+
if (!assertAuthenticated())
|
|
15
|
+
return;
|
|
16
|
+
const log = (...args) => { if (!opts.json)
|
|
17
|
+
console.log(...args); };
|
|
18
|
+
// Must run inside repo (no --path)
|
|
19
|
+
const git = detectGitRepo(process.cwd());
|
|
20
|
+
if (!git.isRepo || !git.root) {
|
|
21
|
+
console.error(colors.error('This command must be run inside a git repository.'));
|
|
22
|
+
process.exitCode = 1;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const derivedRepoName = deriveRepoName(git.root, git.remote);
|
|
26
|
+
if (!derivedRepoName) {
|
|
27
|
+
console.error(colors.error('Unable to determine repo name from git remote.'));
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const commitHashForLookup = String(opts.commit ?? git.head ?? '').trim();
|
|
32
|
+
if (!commitHashForLookup) {
|
|
33
|
+
console.error(colors.error('Unable to determine commit (no commit provided and HEAD is missing).'));
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const fname = `snapshot-${shortCommit(commitHashForLookup)}.json`;
|
|
38
|
+
const manifestPathAbs = path.join(git.root, '.codequill', 'snapshots', fname);
|
|
39
|
+
if (!(await fileExists(manifestPathAbs))) {
|
|
40
|
+
console.error(colors.error('Snapshot manifest not found.'));
|
|
41
|
+
if (!opts.json) {
|
|
42
|
+
console.error(colors.dim(`Expected: ${path.relative(process.cwd(), manifestPathAbs)}`));
|
|
43
|
+
console.error(colors.dim(`Tip: run \`codequill snapshot --commit ${commitHashForLookup || '<hash>'}\` first.`));
|
|
44
|
+
}
|
|
45
|
+
process.exitCode = 1;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// 1) Read + parse manifest
|
|
49
|
+
const sRead = spinner(`Reading manifest: ${path.relative(process.cwd(), manifestPathAbs)} ...`, opts.json);
|
|
50
|
+
let manifestObj;
|
|
51
|
+
let rawBytes;
|
|
52
|
+
try {
|
|
53
|
+
rawBytes = await fs.readFile(manifestPathAbs);
|
|
54
|
+
manifestObj = JSON.parse(rawBytes.toString('utf8'));
|
|
55
|
+
sRead.succeed(colors.success('Manifest loaded.'));
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
sRead.fail(colors.error('Failed to read manifest.'));
|
|
59
|
+
console.error(colors.error(String(e?.message || e)));
|
|
60
|
+
process.exitCode = 1;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// 2) Validate / normalize
|
|
64
|
+
try {
|
|
65
|
+
if (!manifestObj || typeof manifestObj !== 'object')
|
|
66
|
+
throw new Error('Invalid manifest JSON.');
|
|
67
|
+
if (!manifestObj.repo || !manifestObj.commit || !manifestObj.merkle_root || !manifestObj.content_root || !Array.isArray(manifestObj.files)) {
|
|
68
|
+
throw new Error('Invalid manifest: missing required fields (repo, commit, merkle_root, content_root, files).');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
console.error(colors.error(String(e?.message || e)));
|
|
73
|
+
process.exitCode = 1;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const repoNameFromManifest = String(manifestObj.repo ?? '').trim();
|
|
77
|
+
const commitHash = String(manifestObj.commit ?? '').trim();
|
|
78
|
+
const merkleRoot = String(manifestObj.merkle_root ?? '').trim();
|
|
79
|
+
const contentRoot = String(manifestObj.content_root ?? '').trim();
|
|
80
|
+
const version = String(manifestObj.version ?? 'codequill-snapshot:v1');
|
|
81
|
+
if (!repoNameFromManifest || !commitHash || !merkleRoot || !contentRoot) {
|
|
82
|
+
console.error(colors.error('Invalid manifest: missing repo / commit / merkle_root / content_root.'));
|
|
83
|
+
process.exitCode = 1;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Safety: publish only for the repo we’re inside
|
|
87
|
+
if (repoNameFromManifest !== derivedRepoName) {
|
|
88
|
+
console.error(colors.error('Manifest repo does not match current git repo.'));
|
|
89
|
+
if (!opts.json) {
|
|
90
|
+
console.error(colors.dim(`Manifest repo: ${repoNameFromManifest}`));
|
|
91
|
+
console.error(colors.dim(`Git repo: ${derivedRepoName}`));
|
|
92
|
+
console.error(colors.dim('Tip: run `codequill snapshot` inside this repo to generate the correct manifest.'));
|
|
93
|
+
}
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (!opts.noConfirm && !opts.json) {
|
|
98
|
+
const ok = await confirmDangerousAction('You are about to publish a snapshot', [
|
|
99
|
+
` Manifest ${path.relative(process.cwd(), manifestPathAbs)}`,
|
|
100
|
+
` Repo ${repoNameFromManifest}`,
|
|
101
|
+
` Commit ${commitHash}`,
|
|
102
|
+
` Merkle Root ${merkleRoot}`,
|
|
103
|
+
` Files ${Array.isArray(manifestObj.files) ? manifestObj.files.length : 0}`,
|
|
104
|
+
` Version ${version}`,
|
|
105
|
+
], 'This action anchors the snapshot on-chain.');
|
|
106
|
+
if (!ok) {
|
|
107
|
+
log(colors.dim('Publish cancelled.'));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// 3) Gzip manifest bytes for upload
|
|
112
|
+
const sGz = spinner('Compressing manifest (gzip) ...', opts.json);
|
|
113
|
+
let gzBytes;
|
|
114
|
+
try {
|
|
115
|
+
gzBytes = gzipSync(rawBytes, { level: 9 });
|
|
116
|
+
sGz.succeed(colors.success(`Compressed (${rawBytes.length} → ${gzBytes.length} bytes).`));
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
sGz.fail(colors.error('Failed to gzip manifest.'));
|
|
120
|
+
console.error(colors.error(String(e?.message || e)));
|
|
121
|
+
process.exitCode = 1;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// 4) Build multipart
|
|
125
|
+
const form = new FormData();
|
|
126
|
+
form.append('repo_name', repoNameFromManifest);
|
|
127
|
+
form.append('commit_hash', commitHash);
|
|
128
|
+
form.append('merkle_root', merkleRoot);
|
|
129
|
+
form.append('version', version);
|
|
130
|
+
form.append('manifest_encoding', 'gzip');
|
|
131
|
+
form.append('manifest_original_name', path.basename(manifestPathAbs));
|
|
132
|
+
const gzFilename = `${path.basename(manifestPathAbs).replace(/\.json$/i, '') || 'manifest'}.json.gz`;
|
|
133
|
+
const file = new File([new Uint8Array(gzBytes)], gzFilename, { type: 'application/gzip' });
|
|
134
|
+
form.append('manifest', file);
|
|
135
|
+
// 5) Publish
|
|
136
|
+
const sPub = spinner('Publishing ...', opts.json);
|
|
137
|
+
let res;
|
|
138
|
+
try {
|
|
139
|
+
res = await apiClient.postMultipart('/v1/cli/publish', form);
|
|
140
|
+
sPub.succeed(colors.success('Publish submitted.'));
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
sPub.fail(colors.error('Publish failed.'));
|
|
144
|
+
console.error(colors.error(String(e?.message || e)));
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const index = new SnapshotIndex({ repoRoot: git.root, repoName: derivedRepoName });
|
|
150
|
+
await index.load();
|
|
151
|
+
// Ensure manifest lives inside .codequill/snapshots/
|
|
152
|
+
const relFromRepo = path.relative(git.root, manifestPathAbs).split(path.sep).join('/');
|
|
153
|
+
index.upsertFromManifestFile({
|
|
154
|
+
manifestFileRel: relFromRepo,
|
|
155
|
+
manifest: {
|
|
156
|
+
commit: manifestObj.commit,
|
|
157
|
+
merkle_root: manifestObj.merkle_root,
|
|
158
|
+
timestamp: typeof manifestObj.timestamp === 'number' ? manifestObj.timestamp : 0,
|
|
159
|
+
},
|
|
160
|
+
kind: relFromRepo.toLowerCase().includes('published-') ? 'pulled' : 'local',
|
|
161
|
+
});
|
|
162
|
+
const publishedAt = typeof res.published_at === 'number' ? res.published_at : Math.floor(Date.now() / 1000);
|
|
163
|
+
index.attachPublishedByRoot({
|
|
164
|
+
merkle_root: manifestObj.merkle_root,
|
|
165
|
+
meta: {
|
|
166
|
+
snapshot_id: res.snapshot_id,
|
|
167
|
+
published_at: publishedAt,
|
|
168
|
+
tx_hash: res.tx_hash,
|
|
169
|
+
chain_id: res.chain_id,
|
|
170
|
+
manifest_cid: res.manifest_cid,
|
|
171
|
+
wallet_address: res.wallet_address,
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
await index.save();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Index is a cache/helper; publish remains successful even if index fails.
|
|
178
|
+
}
|
|
179
|
+
// 7) Output
|
|
180
|
+
if (opts.json) {
|
|
181
|
+
console.log(JSON.stringify(res, null, 2));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
console.log('');
|
|
185
|
+
console.log(colors.bold('🚀 Publish started'));
|
|
186
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
187
|
+
console.log(colors.dim('Publish'));
|
|
188
|
+
console.log(` Repository ${repoNameFromManifest}`);
|
|
189
|
+
console.log(` Commit ${commitHash}`);
|
|
190
|
+
console.log(` Merkle Root ${merkleRoot}`);
|
|
191
|
+
console.log(` Snapshot URL ${getBaseUrl() + '/s/' + res.snapshot_id}`);
|
|
192
|
+
console.log(` Repo ID ${res.repository_id}`);
|
|
193
|
+
console.log(` Tx Hash ${res.tx_hash}`);
|
|
194
|
+
console.log(` Manifest CID ${res.manifest_cid}`);
|
|
195
|
+
if (res.chain_id)
|
|
196
|
+
console.log(` Chain ID ${res.chain_id}`);
|
|
197
|
+
if (res.wallet_address)
|
|
198
|
+
console.log(` Wallet ${res.wallet_address}`);
|
|
199
|
+
if (res.explorer_url)
|
|
200
|
+
console.log(` Explorer ${res.explorer_url}`);
|
|
201
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
202
|
+
}
|
|
203
|
+
if (opts.noWait) {
|
|
204
|
+
log(colors.dim('Note: skipping confirmation wait (--no-wait).'));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// 8) Wait confirmation
|
|
208
|
+
const confTarget = Math.max(1, Number(opts.confirmations ?? 1));
|
|
209
|
+
try {
|
|
210
|
+
await waitForTxConfirmation({
|
|
211
|
+
txHash: res.tx_hash,
|
|
212
|
+
confirmations: confTarget,
|
|
213
|
+
timeoutMs: opts.timeoutMs,
|
|
214
|
+
silent: opts.json,
|
|
215
|
+
});
|
|
216
|
+
log(colors.success(`✓ Confirmed on-chain (${confTarget} confirmation${confTarget > 1 ? 's' : ''}).`));
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
console.error(colors.error(String(e?.message || e)));
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export function registerPublishCommand(program) {
|
|
224
|
+
program
|
|
225
|
+
.command('publish [commit]')
|
|
226
|
+
.description('Publish a local snapshot manifest (by commit, default HEAD) to IPFS + anchor Merkle root on-chain')
|
|
227
|
+
.option('--no-confirm', 'Skip interactive confirmation')
|
|
228
|
+
.option('--confirmations <n>', 'Wait for N confirmations (default: 1)', (v) => parseInt(v, 10))
|
|
229
|
+
.option('--timeout <ms>', 'Timeout waiting for confirmation (ms)', (v) => parseInt(v, 10))
|
|
230
|
+
.option('--no-wait', 'Do not wait for confirmation')
|
|
231
|
+
.option('--json', 'Output result in JSON format', false)
|
|
232
|
+
.action(async (commit, options) => {
|
|
233
|
+
await handlePublish({
|
|
234
|
+
commit,
|
|
235
|
+
noConfirm: !!options.noConfirm,
|
|
236
|
+
confirmations: options.confirmations,
|
|
237
|
+
timeoutMs: options.timeout,
|
|
238
|
+
noWait: options.wait === false,
|
|
239
|
+
json: !!options.json,
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=publish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { colors, shortCommit, spinner } from '../services/ui.js';
|
|
2
|
+
import { apiClient } from '../services/apiClient.js';
|
|
3
|
+
import { ApiError } from '../services/errors.js';
|
|
4
|
+
import { detectGitRepo, deriveRepoName } from '../services/git.js';
|
|
5
|
+
import { ensureDir, fileExists, writeFileUtf8 } from '../services/fs.js';
|
|
6
|
+
import { SnapshotIndex } from '../services/snapshotIndex.js';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { assertAuthenticated } from "../services/utilities.js";
|
|
9
|
+
function safeFileName(x) {
|
|
10
|
+
return String(x ?? '').replace(/[^a-zA-Z0-9._-]+/g, '-');
|
|
11
|
+
}
|
|
12
|
+
async function fetchPublished(repoName) {
|
|
13
|
+
const res = await apiClient.get(`/v1/cli/snapshots?repo_name=${encodeURIComponent(repoName)}`);
|
|
14
|
+
return Array.isArray(res?.snapshots) ? res.snapshots : [];
|
|
15
|
+
}
|
|
16
|
+
async function fetchManifest(snapshotId) {
|
|
17
|
+
const res = await apiClient.get(`/v1/cli/snapshots/${encodeURIComponent(snapshotId)}/manifest`);
|
|
18
|
+
if (!res?.manifest || typeof res.manifest !== 'object') {
|
|
19
|
+
throw new Error('Invalid backend manifest response.');
|
|
20
|
+
}
|
|
21
|
+
return res.manifest;
|
|
22
|
+
}
|
|
23
|
+
export async function handlePull(_opts) {
|
|
24
|
+
if (!assertAuthenticated())
|
|
25
|
+
return;
|
|
26
|
+
const git = detectGitRepo(process.cwd());
|
|
27
|
+
if (!git.isRepo || !git.root) {
|
|
28
|
+
console.error(colors.error('This command must be run inside a git repository.'));
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const repoName = deriveRepoName(git.root, git.remote);
|
|
33
|
+
if (!repoName) {
|
|
34
|
+
console.error(colors.error('Unable to determine repository name from git remote.'));
|
|
35
|
+
console.error(colors.dim('Make sure you have a GitHub origin remote configured.'));
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Ensure snapshots folder exists
|
|
40
|
+
const snapshotsDirAbs = path.join(git.root, '.codequill', 'snapshots');
|
|
41
|
+
await ensureDir(snapshotsDirAbs);
|
|
42
|
+
// Load index (will rebuild from disk if missing/corrupt)
|
|
43
|
+
const index = new SnapshotIndex({ repoRoot: git.root, repoName });
|
|
44
|
+
await index.load();
|
|
45
|
+
// Fetch published snapshots
|
|
46
|
+
const s = spinner(`Fetching published snapshots for ${repoName} ...`);
|
|
47
|
+
let published = [];
|
|
48
|
+
try {
|
|
49
|
+
published = await fetchPublished(repoName);
|
|
50
|
+
s.succeed(colors.success(`Found ${published.length} published snapshot${published.length === 1 ? '' : 's'}.`));
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
s.fail(colors.error('Failed to fetch published snapshots.'));
|
|
54
|
+
if (e instanceof ApiError && e.status === 401) {
|
|
55
|
+
console.error(colors.error('Unauthorized. Run `codequill login` again.'));
|
|
56
|
+
}
|
|
57
|
+
else if (e instanceof ApiError) {
|
|
58
|
+
console.error(colors.error(`${e.message} (${e.status})`));
|
|
59
|
+
if (e.requestId)
|
|
60
|
+
console.error(colors.dim(`Request ID: ${e.requestId}`));
|
|
61
|
+
if (e.method && e.url)
|
|
62
|
+
console.error(colors.dim(`Endpoint: ${e.method} ${e.url}`));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.error(colors.error(String(e?.message || e)));
|
|
66
|
+
}
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (published.length === 0) {
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(colors.warn('⚠ No published snapshots found for this repository.'));
|
|
73
|
+
console.log(colors.dim('Hint: run `codequill publish <manifest>` to anchor your first snapshot on-chain.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// Sort newest first (nice UX + consistent latest)
|
|
77
|
+
published.sort((a, b) => (Number(b.published_at ?? 0) - Number(a.published_at ?? 0)));
|
|
78
|
+
// Enrich index with backend published list (adds published meta + remote-only items)
|
|
79
|
+
index.enrichFromBackend(published);
|
|
80
|
+
// Determine which ones we need to download:
|
|
81
|
+
// - remote-only items, OR items whose file is missing on disk
|
|
82
|
+
const items = index.list();
|
|
83
|
+
const wantByRoot = new Map();
|
|
84
|
+
for (const it of items) {
|
|
85
|
+
if (!it.published)
|
|
86
|
+
continue;
|
|
87
|
+
const root = it.merkle_root;
|
|
88
|
+
if (!root)
|
|
89
|
+
continue;
|
|
90
|
+
const snapshotId = String(it.published.snapshot_id ?? '').trim();
|
|
91
|
+
if (!snapshotId)
|
|
92
|
+
continue;
|
|
93
|
+
const hasFile = !!it.file;
|
|
94
|
+
let fileOk = false;
|
|
95
|
+
if (hasFile && it.file) {
|
|
96
|
+
const abs = path.join(git.root, it.file);
|
|
97
|
+
fileOk = await fileExists(abs);
|
|
98
|
+
}
|
|
99
|
+
if (!hasFile || !fileOk) {
|
|
100
|
+
// Find commit hash from backend list (more reliable)
|
|
101
|
+
const pub = published.find((p) => String(p.snapshot_id) === snapshotId);
|
|
102
|
+
const commitHash = String(pub?.commit_hash ?? it.commit ?? '').trim();
|
|
103
|
+
wantByRoot.set(root, { snapshot_id: snapshotId, commit_hash: commitHash });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const toPull = Array.from(wantByRoot.values());
|
|
107
|
+
console.log('');
|
|
108
|
+
console.log(colors.bold('⬇️ CodeQuill Pull'));
|
|
109
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
110
|
+
console.log(`Repository ${repoName}`);
|
|
111
|
+
console.log(`Published ${published.length}`);
|
|
112
|
+
console.log(`To download ${toPull.length}`);
|
|
113
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
114
|
+
if (toPull.length === 0) {
|
|
115
|
+
console.log(colors.success('✔ Nothing to pull. Published history is already present locally.'));
|
|
116
|
+
// still save index because enrichFromBackend might have updated ordering/latest
|
|
117
|
+
await index.save();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let ok = 0;
|
|
121
|
+
let fail = 0;
|
|
122
|
+
for (const p of toPull) {
|
|
123
|
+
const label = `${p.snapshot_id} (${shortCommit(p.commit_hash)})`;
|
|
124
|
+
const sOne = spinner(`Downloading manifest ${label} ...`);
|
|
125
|
+
try {
|
|
126
|
+
const manifest = await fetchManifest(p.snapshot_id);
|
|
127
|
+
const cShort = safeFileName(shortCommit(p.commit_hash || manifest.commit));
|
|
128
|
+
const fname = `snapshot-${cShort}.json`;
|
|
129
|
+
const outAbs = path.join(snapshotsDirAbs, fname);
|
|
130
|
+
const outRel = path.relative(git.root, outAbs).split(path.sep).join('/'); // normalize to forward slashes
|
|
131
|
+
await writeFileUtf8(outAbs, JSON.stringify(manifest, null, 2));
|
|
132
|
+
// Upsert disk item into index (no full rescan required)
|
|
133
|
+
index.upsertFromManifestFile({
|
|
134
|
+
manifestFileRel: outRel,
|
|
135
|
+
manifest: {
|
|
136
|
+
commit: manifest.commit,
|
|
137
|
+
merkle_root: manifest.merkle_root,
|
|
138
|
+
timestamp: manifest.timestamp,
|
|
139
|
+
},
|
|
140
|
+
kind: 'pulled',
|
|
141
|
+
});
|
|
142
|
+
sOne.succeed(colors.success(`Saved: ${path.relative(process.cwd(), outAbs)}`));
|
|
143
|
+
ok += 1;
|
|
144
|
+
}
|
|
145
|
+
catch (e) {
|
|
146
|
+
sOne.fail(colors.error(`Failed: ${label}`));
|
|
147
|
+
console.error(colors.dim(String(e?.message || e)));
|
|
148
|
+
fail += 1;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// One save at end (fast + consistent)
|
|
152
|
+
const sIdx = spinner('Updating index ...');
|
|
153
|
+
try {
|
|
154
|
+
await index.save();
|
|
155
|
+
sIdx.succeed(colors.success('Index updated.'));
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
sIdx.fail(colors.warn('Index update failed (snapshots are still downloaded).'));
|
|
159
|
+
console.error(colors.dim(String(e?.message || e)));
|
|
160
|
+
}
|
|
161
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
162
|
+
console.log(`${colors.success('Downloaded')} ${ok}`);
|
|
163
|
+
if (fail)
|
|
164
|
+
console.log(`${colors.error('Failed')} ${fail}`);
|
|
165
|
+
}
|
|
166
|
+
export function registerPullCommand(program) {
|
|
167
|
+
program
|
|
168
|
+
.command('pull')
|
|
169
|
+
.description('Download all published snapshot manifests for the current repository into .codequill/snapshots')
|
|
170
|
+
.action(async () => {
|
|
171
|
+
await handlePull({});
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=pull.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/commands/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { apiClient } from '../services/apiClient.js';
|
|
2
|
+
import { colors, spinner } from '../services/ui.js';
|
|
3
|
+
import { ApiError } from '../services/errors.js';
|
|
4
|
+
import { deriveRepoName, detectGitRepo } from "../services/git.js";
|
|
5
|
+
import { assertAuthenticated } from "../services/utilities.js";
|
|
6
|
+
export async function handleQuota(opts) {
|
|
7
|
+
if (!assertAuthenticated())
|
|
8
|
+
return;
|
|
9
|
+
const git = detectGitRepo(process.cwd());
|
|
10
|
+
if (!git.isRepo || !git.root) {
|
|
11
|
+
console.error(colors.error('This command must be run inside a git repository.'));
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const repoName = deriveRepoName(git.root, git.remote);
|
|
16
|
+
if (!repoName) {
|
|
17
|
+
console.error(colors.error('Unable to determine repository name from git remote.'));
|
|
18
|
+
console.error(colors.dim('Make sure you have a GitHub origin remote configured.'));
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const s = spinner(`Loading CodeQuill quota for ${repoName} ...`);
|
|
23
|
+
let quota;
|
|
24
|
+
try {
|
|
25
|
+
const q = encodeURIComponent(repoName);
|
|
26
|
+
quota = await apiClient.get(`/v1/cli/quota?repo_name=${q}`);
|
|
27
|
+
s.succeed(colors.success('Quota loaded.'));
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
s.fail(colors.error('Failed to load quota.'));
|
|
31
|
+
if (e instanceof ApiError && e.status === 401) {
|
|
32
|
+
console.error(colors.error('Unauthorized. Run `codequill login` again.'));
|
|
33
|
+
}
|
|
34
|
+
else if (e instanceof ApiError) {
|
|
35
|
+
console.error(colors.error(`${e.message} (${e.status})`));
|
|
36
|
+
if (e.requestId)
|
|
37
|
+
console.error(colors.dim(`Request ID: ${e.requestId}`));
|
|
38
|
+
if (e.method && e.url)
|
|
39
|
+
console.error(colors.dim(`Endpoint: ${e.method} ${e.url}`));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
console.error(colors.error(String(e?.message || e)));
|
|
43
|
+
}
|
|
44
|
+
process.exitCode = 1;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (opts.json) {
|
|
48
|
+
const payload = {
|
|
49
|
+
workspace_name: quota.workspace_name,
|
|
50
|
+
plan_code: quota.plan_code,
|
|
51
|
+
claim_public_limit: quota.claim_public_limit,
|
|
52
|
+
claim_private_limit: quota.claim_private_limit,
|
|
53
|
+
api_repository_limit: quota.api_repository_limit,
|
|
54
|
+
collaborator_limit: quota.collaborator_limit,
|
|
55
|
+
publish_monthly_limit: quota.publish_monthly_limit,
|
|
56
|
+
attestation_monthly_limit: quota.attestation_monthly_limit,
|
|
57
|
+
claim_public_used: quota.claim_public_used,
|
|
58
|
+
claim_private_used: quota.claim_private_used,
|
|
59
|
+
api_repository_used: quota.api_repository_used,
|
|
60
|
+
collaborator_used: quota.collaborator_used,
|
|
61
|
+
publish_used: quota.publish_used,
|
|
62
|
+
attestation_used: quota.attestation_used
|
|
63
|
+
};
|
|
64
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.log(colors.bold('Workspace:'), colors.info(quota.workspace_name));
|
|
68
|
+
console.log(colors.bold('Subscription plan:'), colors.info(quota.plan_code));
|
|
69
|
+
console.log('\nClaimed repositories:');
|
|
70
|
+
console.log(` - Public: ${quota.claim_public_used} / ${quota.claim_public_limit}`);
|
|
71
|
+
console.log(` - Private: ${quota.claim_private_used} / ${quota.claim_private_limit}`);
|
|
72
|
+
console.log('\nCI integrations:');
|
|
73
|
+
console.log(` - Used: ${quota.api_repository_used} / ${quota.api_repository_limit}`);
|
|
74
|
+
console.log('\nSnapshots:');
|
|
75
|
+
console.log(` - Used this month: ${quota.publish_used} / ${quota.publish_monthly_limit}`);
|
|
76
|
+
console.log('\nAttestations:');
|
|
77
|
+
console.log(` - Used this month: ${quota.attestation_used} / ${quota.attestation_monthly_limit}`);
|
|
78
|
+
console.log('\nBackups:');
|
|
79
|
+
console.log(` - Used this month: ${quota.backup_used} / ${quota.backup_monthly_limit}`);
|
|
80
|
+
console.log('\nCollaborators:');
|
|
81
|
+
console.log(` - Used : ${quota.collaborator_used} / ${quota.collaborator_limit}`);
|
|
82
|
+
}
|
|
83
|
+
export function registerQuotaCommand(program) {
|
|
84
|
+
program
|
|
85
|
+
.command('quota')
|
|
86
|
+
.description('Show your subscription plan and quota usage for the current workspace')
|
|
87
|
+
.option('--json', 'Output machine-readable JSON', false)
|
|
88
|
+
.action(async (options) => {
|
|
89
|
+
await handleQuota({
|
|
90
|
+
json: !!options.json
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/commands/quota.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { colors, spinner } from '../services/ui.js';
|
|
2
|
+
import { apiClient } from '../services/apiClient.js';
|
|
3
|
+
import { assertAuthenticated } from '../services/utilities.js';
|
|
4
|
+
import { waitForTxConfirmation } from '../services/txWaiter.js';
|
|
5
|
+
import { getBaseUrl } from '../services/config.js';
|
|
6
|
+
export async function handleRevoke(attestationId, opts) {
|
|
7
|
+
if (!assertAuthenticated())
|
|
8
|
+
return;
|
|
9
|
+
const id = String(attestationId || '').trim();
|
|
10
|
+
if (!id) {
|
|
11
|
+
console.error(colors.error('Missing <attestationId>.'));
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// 1) Call backend (backend resolves uuid -> (repoId,digest,type) + author wallet and relays revoke tx)
|
|
16
|
+
const s = spinner('Revoking attestation ...');
|
|
17
|
+
let res;
|
|
18
|
+
try {
|
|
19
|
+
// Keep payload explicit. Backend can ignore fields it doesn't support yet.
|
|
20
|
+
const body = {
|
|
21
|
+
attestation_id: id,
|
|
22
|
+
// Off-chain context (DB + UI)
|
|
23
|
+
reason: opts.reason ? String(opts.reason) : undefined,
|
|
24
|
+
replacement_attestation_id: opts.replacement ? String(opts.replacement) : undefined,
|
|
25
|
+
// Optional on-chain context (if your contract includes these)
|
|
26
|
+
note_cid: opts.noteCid ? String(opts.noteCid) : undefined,
|
|
27
|
+
onchain_reason: typeof opts.onchainReason === 'number' ? opts.onchainReason : undefined,
|
|
28
|
+
};
|
|
29
|
+
res = await apiClient.post('/v1/cli/attest/revoke', body);
|
|
30
|
+
if (!res || !res.attestation_id)
|
|
31
|
+
throw new Error('Malformed revoke response.');
|
|
32
|
+
s.succeed(colors.success(res.status === 'already_revoked' ? 'Already revoked.' : 'Revocation submitted.'));
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
s.fail(colors.error('Revocation failed.'));
|
|
36
|
+
console.error(colors.error(String(e?.message || e)));
|
|
37
|
+
process.exitCode = 1;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// 2) Output
|
|
41
|
+
console.log('');
|
|
42
|
+
console.log(colors.bold('🧯 Attestation revocation'));
|
|
43
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
44
|
+
console.log(` Attestation ID ${res.attestation_id}`);
|
|
45
|
+
console.log(` Status ${res.status}`);
|
|
46
|
+
console.log(` URL ${getBaseUrl() + '/a/' + res.attestation_id}`);
|
|
47
|
+
if (res.tx_hash)
|
|
48
|
+
console.log(` Tx Hash ${res.tx_hash}`);
|
|
49
|
+
if (res.chain_id)
|
|
50
|
+
console.log(` Chain ID ${res.chain_id}`);
|
|
51
|
+
if (res.explorer_url)
|
|
52
|
+
console.log(` Explorer ${res.explorer_url}`);
|
|
53
|
+
console.log(colors.dim('────────────────────────────────────────'));
|
|
54
|
+
// 3) Wait confirmation (if tx returned and user wants it)
|
|
55
|
+
if (opts.noWait || !res.tx_hash) {
|
|
56
|
+
if (!res.tx_hash)
|
|
57
|
+
console.log(colors.dim('Note: no tx hash returned (revocation may be purely off-chain or queued).'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const confTarget = Math.max(1, Number(opts.confirmations ?? 1));
|
|
61
|
+
try {
|
|
62
|
+
await waitForTxConfirmation({
|
|
63
|
+
txHash: res.tx_hash,
|
|
64
|
+
confirmations: confTarget,
|
|
65
|
+
timeoutMs: opts.timeoutMs,
|
|
66
|
+
});
|
|
67
|
+
console.log(colors.success(`✓ Confirmed on-chain (${confTarget} confirmation${confTarget > 1 ? 's' : ''}).`));
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
console.error(colors.error(String(e?.message || e)));
|
|
71
|
+
process.exitCode = 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export function registerRevokeCommand(program) {
|
|
75
|
+
program
|
|
76
|
+
.command('revoke <attestationId>')
|
|
77
|
+
.description('Revoke an attestation (records revocation; does not delete).')
|
|
78
|
+
.option('--reason <text>', 'Human-readable reason (stored off-chain)')
|
|
79
|
+
.option('--replacement <attestationId>', 'Replacement attestation UUID (stored off-chain)')
|
|
80
|
+
.option('--note-cid <cid>', 'Optional public CID with revocation note')
|
|
81
|
+
.option('--onchain-reason <n>', 'Optional on-chain reason code (uint8)', (v) => parseInt(v, 10))
|
|
82
|
+
.option('--confirmations <n>', 'Wait for N confirmations (default: 1)', (v) => parseInt(v, 10))
|
|
83
|
+
.option('--timeout <ms>', 'Timeout waiting for confirmation (ms)', (v) => parseInt(v, 10))
|
|
84
|
+
.option('--no-wait', 'Do not wait for confirmation', false)
|
|
85
|
+
.action(async (attestationId, options) => {
|
|
86
|
+
await handleRevoke(attestationId, {
|
|
87
|
+
reason: options.reason,
|
|
88
|
+
replacement: options.replacement,
|
|
89
|
+
noteCid: options.noteCid,
|
|
90
|
+
onchainReason: Number.isFinite(options.onchainReason) ? options.onchainReason : undefined,
|
|
91
|
+
confirmations: options.confirmations,
|
|
92
|
+
timeoutMs: options.timeout,
|
|
93
|
+
noWait: !!options.noWait,
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=revoke.js.map
|