@dvmkit/dvmctl 0.3.1-rc.1 → 0.3.3-rc.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dvmkit/dvmctl",
3
- "version": "0.3.1-rc.1",
3
+ "version": "0.3.3-rc.1",
4
4
  "description": "Builder CLI for creating, testing, and deploying Digital Vending Machines",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -84,18 +84,45 @@ dvm request --endpoint "$dvm_endpoint" --data "$DVM_INPUT" --rail cashu --mint "
84
84
  --max-increment "$DVM_PAYMENT_CAP" --max-payments 1 </dev/null >"$test_root/paid.json"
85
85
  dvm wallet show >"$test_root/after.json"
86
86
  job_id="$(node -p 'require(process.argv[1]).jobId' "$test_root/paid.json")"
87
- dvm receipts verify "$job_id" >"$test_root/verified.json"
88
- node - "$test_root/unpaid.json" "$test_root/before.json" "$test_root/after.json" "$test_root/paid.json" "$test_root/verified.json" <<'NODE'
89
- const [challengePath, beforePath, afterPath, paidPath, verifiedPath] = process.argv.slice(2);
90
- const challenge=require(challengePath), before=require(beforePath), after=require(afterPath), paid=require(paidPath), verified=require(verifiedPath);
87
+ result_dir="$PWD/.dvmkit-test-results/$job_id"
88
+ mkdir -p -m 700 "$result_dir"
89
+ cp "$test_root/unpaid.json" "$test_root/before.json" "$test_root/after.json" "$test_root/paid.json" "$result_dir/"
90
+ dvm messages "$job_id" --no-stream >/dev/null
91
+ TMPDIR="$result_dir" dvm messages "$job_id" --no-stream --human | tee "$result_dir/artifacts.txt"
92
+ retained_config="$result_dir/caller"
93
+ node - "$DVM_CONFIG_DIR" "$retained_config" "$result_dir/messages.json" "$job_id" <<'NODE'
94
+ const { mkdirSync, readFileSync, writeFileSync } = require("node:fs");
95
+ const { join } = require("node:path");
96
+ const [source, destination, resultPath, jobId] = process.argv.slice(2);
97
+ const job = JSON.parse(readFileSync(join(source, "jobs.json"), "utf8")).find(item => item.id === jobId);
98
+ if (!job) throw Error(`caller state did not retain ${jobId}`);
99
+ const receipts = readFileSync(join(source, "receipts.jsonl"), "utf8").trim().split("\n").filter(Boolean).filter(line => JSON.parse(line).receipt?.job_id === jobId);
100
+ if (receipts.length !== 1) throw Error(`expected one receipt for ${jobId}, found ${receipts.length}`);
101
+ const messages = readFileSync(join(source, "messages", `${jobId}.jsonl`), "utf8").trim().split("\n").filter(Boolean).map(line => JSON.parse(line));
102
+ const retainedMessages = messages.filter(message => ["text", "artifact", "complete", "cancel"].includes(message.type));
103
+ if (!retainedMessages.some(message => message.type === "complete" || message.type === "cancel")) throw Error("caller state did not retain a terminal message");
104
+ const retainedJob = { id: job.id, endpoint: job.endpoint, status: job.status, submittedAt: job.submittedAt, completedAt: job.completedAt, completion: job.completion, paymentsHonoured: job.paymentsHonoured, policy: job.policy };
105
+ mkdirSync(join(destination, "messages"), { recursive: true, mode: 0o700 });
106
+ writeFileSync(join(destination, "jobs.json"), `${JSON.stringify([retainedJob])}\n`, { mode: 0o600 });
107
+ writeFileSync(join(destination, "receipts.jsonl"), `${receipts[0]}\n`, { mode: 0o600 });
108
+ writeFileSync(join(destination, "messages", `${jobId}.jsonl`), `${retainedMessages.map(message => JSON.stringify(message)).join("\n")}\n`, { mode: 0o600 });
109
+ writeFileSync(resultPath, `${JSON.stringify({ jobId, messages: retainedMessages })}\n`, { mode: 0o600 });
110
+ NODE
111
+ DVM_CONFIG_DIR="$retained_config" dvm receipts verify "$job_id" >"$result_dir/verified.json"
112
+ node - "$result_dir/unpaid.json" "$result_dir/before.json" "$result_dir/after.json" "$result_dir/paid.json" "$result_dir/messages.json" "$result_dir/verified.json" <<'NODE'
113
+ const [challengePath, beforePath, afterPath, paidPath, messagesPath, verifiedPath] = process.argv.slice(2);
114
+ const challenge=require(challengePath), before=require(beforePath), after=require(afterPath), paid=require(paidPath), messages=require(messagesPath), verified=require(verifiedPath);
91
115
  const validReceiptStatuses = new Set(["verified", "verified_unattested"]);
92
116
  const expectedSats=challenge.required_msats/1000;
93
117
  if (before.total_sats-after.total_sats !== expectedSats) throw Error(`Cashu debit was ${before.total_sats-after.total_sats}, quote was ${expectedSats} sats`);
94
118
  if (!paid.jobId || paid.next_action !== null || !validReceiptStatuses.has(paid.receipt_verified)) throw Error("paid result is not terminal with a valid receipt signature");
95
119
  if (paid.receipt?.outcome !== "completed" || paid.receipt?.paid?.rail !== "cashu" || paid.receipt.paid.msats !== challenge.required_msats) throw Error("receipt is not bound to the 402 quote");
96
- if (verified.job_id !== paid.jobId || !validReceiptStatuses.has(verified.receipt_verified)) throw Error("stored receipt signature did not verify");
120
+ if (!messages.messages?.some(message => message.type === "complete")) throw Error("paid messages did not complete");
121
+ if ((paid.has_artifacts || paid.artifact_count > 0) && !messages.messages?.some(message => message.type === "artifact")) throw Error("paid result advertised an artifact that was not retrieved");
122
+ if (verified.job_id !== paid.jobId || !validReceiptStatuses.has(verified.receipt_verified) || verified.receipt_checks?.result_hash !== "ok") throw Error("stored receipt and paid result hash did not verify");
97
123
  NODE
98
- cat "$test_root/paid.json"
124
+ cat "$result_dir/paid.json"
125
+ printf '\nPaid result and artifact evidence retained in %s\n' "$result_dir"
99
126
  ```
100
127
 
101
128
  The observed hosted-mint acceptance for a `$0.01` call quoted `12000` msats and debited `12` test sats. That is evidence from that rate snapshot, not a constant for future runs.
@@ -56,17 +56,34 @@ dvm request --endpoint "$dvm_endpoint" --data "$DVM_INPUT" --rail tempo \
56
56
  --budget "$DVM_PAYMENT_CAP" --auto-pay-below "$DVM_PAYMENT_CAP" \
57
57
  --max-increment "$DVM_PAYMENT_CAP" --max-payments 1 </dev/null >"$test_root/paid.json"
58
58
  job_id="$(node -p 'require(process.argv[1]).jobId' "$test_root/paid.json")"
59
- dvm receipts verify "$job_id" >"$test_root/verified.json"
60
- node - "$test_root/unpaid.json" "$test_root/paid.json" "$test_root/verified.json" <<'NODE'
61
- const [challengePath,paidPath,verifiedPath]=process.argv.slice(2);const challenge=require(challengePath),paid=require(paidPath),verified=require(verifiedPath);
59
+ result_dir="$PWD/.dvmkit-test-results/$job_id"
60
+ mkdir -p -m 700 "$result_dir"
61
+ cp "$test_root/unpaid.json" "$test_root/paid.json" "$result_dir/"
62
+ dvm messages "$job_id" --no-stream >/dev/null
63
+ TMPDIR="$result_dir" dvm messages "$job_id" --no-stream --human | tee "$result_dir/artifacts.txt"
64
+ retained_config="$result_dir/caller"
65
+ node - "$DVM_CONFIG_DIR" "$retained_config" "$result_dir/messages.json" "$job_id" <<'NODE'
66
+ const { mkdirSync, readFileSync, writeFileSync }=require("node:fs");const { join }=require("node:path");const [source,destination,resultPath,jobId]=process.argv.slice(2);
67
+ const job=JSON.parse(readFileSync(join(source,"jobs.json"),"utf8")).find(item=>item.id===jobId);if(!job)throw Error(`caller state did not retain ${jobId}`);
68
+ const receipts=readFileSync(join(source,"receipts.jsonl"),"utf8").trim().split("\n").filter(Boolean).filter(line=>JSON.parse(line).receipt?.job_id===jobId);if(receipts.length!==1)throw Error(`expected one receipt for ${jobId}, found ${receipts.length}`);
69
+ const messages=readFileSync(join(source,"messages",`${jobId}.jsonl`),"utf8").trim().split("\n").filter(Boolean).map(line=>JSON.parse(line));const retainedMessages=messages.filter(message=>["text","artifact","complete","cancel"].includes(message.type));if(!retainedMessages.some(message=>message.type==="complete"||message.type==="cancel"))throw Error("caller state did not retain a terminal message");
70
+ const retainedJob={id:job.id,endpoint:job.endpoint,status:job.status,submittedAt:job.submittedAt,completedAt:job.completedAt,completion:job.completion,paymentsHonoured:job.paymentsHonoured,policy:job.policy};mkdirSync(join(destination,"messages"),{recursive:true,mode:0o700});
71
+ writeFileSync(join(destination,"jobs.json"),`${JSON.stringify([retainedJob])}\n`,{mode:0o600});writeFileSync(join(destination,"receipts.jsonl"),`${receipts[0]}\n`,{mode:0o600});writeFileSync(join(destination,"messages",`${jobId}.jsonl`),`${retainedMessages.map(message=>JSON.stringify(message)).join("\n")}\n`,{mode:0o600});writeFileSync(resultPath,`${JSON.stringify({jobId,messages:retainedMessages})}\n`,{mode:0o600});
72
+ NODE
73
+ DVM_CONFIG_DIR="$retained_config" dvm receipts verify "$job_id" >"$result_dir/verified.json"
74
+ node - "$result_dir/unpaid.json" "$result_dir/paid.json" "$result_dir/messages.json" "$result_dir/verified.json" <<'NODE'
75
+ const [challengePath,paidPath,messagesPath,verifiedPath]=process.argv.slice(2);const challenge=require(challengePath),paid=require(paidPath),messages=require(messagesPath),verified=require(verifiedPath);
62
76
  const validReceiptStatuses=new Set(["verified","verified_unattested"]);
63
77
  if(!paid.jobId || paid.next_action!==null || !validReceiptStatuses.has(paid.receipt_verified)) throw Error("paid result is not terminal with a valid receipt signature");
64
78
  const receipt=paid.receipt;
65
79
  if(receipt?.outcome!=="completed" || receipt?.paid?.rail!=="tempo" || receipt.paid.msats!==challenge.required_msats) throw Error("receipt is not quote-bound Tempo");
66
80
  if(receipt.paid.native_amount!==10000 || receipt.paid.native_asset!=="usdc") throw Error("$0.01 must settle as exactly 10000 pathUSD micro-units");
67
- if(verified.job_id!==paid.jobId || !validReceiptStatuses.has(verified.receipt_verified)) throw Error("stored receipt signature did not verify");
81
+ if(!messages.messages?.some(message=>message.type==="complete")) throw Error("paid messages did not complete");
82
+ if((paid.has_artifacts || paid.artifact_count>0) && !messages.messages?.some(message=>message.type==="artifact")) throw Error("paid result advertised an artifact that was not retrieved");
83
+ if(verified.job_id!==paid.jobId || !validReceiptStatuses.has(verified.receipt_verified) || verified.receipt_checks?.result_hash!=="ok") throw Error("stored receipt and paid result hash did not verify");
68
84
  NODE
69
- cat "$test_root/paid.json"
85
+ cat "$result_dir/paid.json"
86
+ printf '\nPaid result and artifact evidence retained in %s\n' "$result_dir"
70
87
  ```
71
88
 
72
89
  Stop if the Moderato chain check, faucet, balance, or cap fails. Do not switch to mainnet or fund a production wallet as a workaround.
@@ -58,17 +58,34 @@ dvm request --endpoint "$dvm_endpoint" --data "$DVM_INPUT" --rail x402 \
58
58
  --budget "$DVM_PAYMENT_CAP" --auto-pay-below "$DVM_PAYMENT_CAP" \
59
59
  --max-increment "$DVM_PAYMENT_CAP" --max-payments 1 </dev/null >"$test_root/paid.json"
60
60
  job_id="$(node -p 'require(process.argv[1]).jobId' "$test_root/paid.json")"
61
- dvm receipts verify "$job_id" >"$test_root/verified.json"
62
- node - "$test_root/unpaid.json" "$test_root/paid.json" "$test_root/verified.json" <<'NODE'
63
- const [challengePath,paidPath,verifiedPath]=process.argv.slice(2);const challenge=require(challengePath),paid=require(paidPath),verified=require(verifiedPath);
61
+ result_dir="$PWD/.dvmkit-test-results/$job_id"
62
+ mkdir -p -m 700 "$result_dir"
63
+ cp "$test_root/unpaid.json" "$test_root/paid.json" "$result_dir/"
64
+ dvm messages "$job_id" --no-stream >/dev/null
65
+ TMPDIR="$result_dir" dvm messages "$job_id" --no-stream --human | tee "$result_dir/artifacts.txt"
66
+ retained_config="$result_dir/caller"
67
+ node - "$DVM_CONFIG_DIR" "$retained_config" "$result_dir/messages.json" "$job_id" <<'NODE'
68
+ const { mkdirSync, readFileSync, writeFileSync }=require("node:fs");const { join }=require("node:path");const [source,destination,resultPath,jobId]=process.argv.slice(2);
69
+ const job=JSON.parse(readFileSync(join(source,"jobs.json"),"utf8")).find(item=>item.id===jobId);if(!job)throw Error(`caller state did not retain ${jobId}`);
70
+ const receipts=readFileSync(join(source,"receipts.jsonl"),"utf8").trim().split("\n").filter(Boolean).filter(line=>JSON.parse(line).receipt?.job_id===jobId);if(receipts.length!==1)throw Error(`expected one receipt for ${jobId}, found ${receipts.length}`);
71
+ const messages=readFileSync(join(source,"messages",`${jobId}.jsonl`),"utf8").trim().split("\n").filter(Boolean).map(line=>JSON.parse(line));const retainedMessages=messages.filter(message=>["text","artifact","complete","cancel"].includes(message.type));if(!retainedMessages.some(message=>message.type==="complete"||message.type==="cancel"))throw Error("caller state did not retain a terminal message");
72
+ const retainedJob={id:job.id,endpoint:job.endpoint,status:job.status,submittedAt:job.submittedAt,completedAt:job.completedAt,completion:job.completion,paymentsHonoured:job.paymentsHonoured,policy:job.policy};mkdirSync(join(destination,"messages"),{recursive:true,mode:0o700});
73
+ writeFileSync(join(destination,"jobs.json"),`${JSON.stringify([retainedJob])}\n`,{mode:0o600});writeFileSync(join(destination,"receipts.jsonl"),`${receipts[0]}\n`,{mode:0o600});writeFileSync(join(destination,"messages",`${jobId}.jsonl`),`${retainedMessages.map(message=>JSON.stringify(message)).join("\n")}\n`,{mode:0o600});writeFileSync(resultPath,`${JSON.stringify({jobId,messages:retainedMessages})}\n`,{mode:0o600});
74
+ NODE
75
+ DVM_CONFIG_DIR="$retained_config" dvm receipts verify "$job_id" >"$result_dir/verified.json"
76
+ node - "$result_dir/unpaid.json" "$result_dir/paid.json" "$result_dir/messages.json" "$result_dir/verified.json" <<'NODE'
77
+ const [challengePath,paidPath,messagesPath,verifiedPath]=process.argv.slice(2);const challenge=require(challengePath),paid=require(paidPath),messages=require(messagesPath),verified=require(verifiedPath);
64
78
  const validReceiptStatuses=new Set(["verified","verified_unattested"]);
65
79
  if(!paid.jobId || paid.next_action!==null || !validReceiptStatuses.has(paid.receipt_verified)) throw Error("paid result is not terminal with a valid receipt signature");
66
80
  const receipt=paid.receipt;
67
81
  if(receipt?.outcome!=="completed" || receipt?.paid?.rail!=="x402" || receipt.paid.msats!==challenge.required_msats) throw Error("receipt is not quote-bound x402");
68
82
  if(receipt.paid.native_amount!==10000 || receipt.paid.native_asset!=="usdc") throw Error("$0.01 must settle as exactly 10000 micro-USDC");
69
- if(verified.job_id!==paid.jobId || !validReceiptStatuses.has(verified.receipt_verified)) throw Error("stored receipt signature did not verify");
83
+ if(!messages.messages?.some(message=>message.type==="complete")) throw Error("paid messages did not complete");
84
+ if((paid.has_artifacts || paid.artifact_count>0) && !messages.messages?.some(message=>message.type==="artifact")) throw Error("paid result advertised an artifact that was not retrieved");
85
+ if(verified.job_id!==paid.jobId || !validReceiptStatuses.has(verified.receipt_verified) || verified.receipt_checks?.result_hash!=="ok") throw Error("stored receipt and paid result hash did not verify");
70
86
  NODE
71
- cat "$test_root/paid.json"
87
+ cat "$result_dir/paid.json"
88
+ printf '\nPaid result and artifact evidence retained in %s\n' "$result_dir"
72
89
  ```
73
90
 
74
91
  Stop on a missing balance, RPC/facilitator failure, or cap refusal. Do not replace the network, key, or cap without new approval.